Skip to content

Instantly share code, notes, and snippets.

@milseman
Created May 3, 2019 22:25
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save milseman/bd7547d87a708a2d8dcec663596354ba to your computer and use it in GitHub Desktop.
Broken: Offset Indexing, Existing ranges, Phantom typed
// TODO: doc
public struct OffsetBound<Bound> {
internal enum Kind {
case fromStart(Int)
case fromEnd(Int)
}
internal var kind: Kind
public init(fromStart: Int) {
self.kind = .fromStart(fromStart)
}
public init(fromEnd: Int) {
self.kind = .fromEnd(fromEnd)
}
}
extension OffsetBound {
// TODO: doc
public func advanced(by: Int) -> OffsetBound<Bound> {
switch self.kind {
case .fromStart(let offset): return OffsetBound(fromStart: offset + by)
case .fromEnd(let offset): return OffsetBound(fromEnd: offset + by)
}
}
// TODO: doc
public func relative<C: Collection>(
to c: C
) -> C.Index where Bound == C.Index {
switch self.kind {
case .fromStart(let int):
if int < 0 { return c.startIndex }
return c.index(
c.startIndex, offsetBy: int, limitedBy: c.endIndex
) ?? c.endIndex
case .fromEnd(let int):
if int > 0 { return c.endIndex }
return c.index(
c.endIndex, offsetBy: int, limitedBy: c.startIndex
) ?? c.startIndex
}
}
}
extension OffsetBound: ExpressibleByIntegerLiteral {
// TODO: doc
public init(integerLiteral int: Int) {
if int < 0 { self.init(fromEnd: int) } else { self.init(fromStart: int) }
}
}
extension OffsetBound: Comparable {
public static func < (_ lhs: OffsetBound, _ rhs: OffsetBound) -> Bool {
switch (lhs.kind, rhs.kind) {
case (.fromStart(_), .fromEnd(_)): return true
case (.fromEnd(_), .fromStart(_)): return false
case (.fromStart(let lhs), .fromStart(let rhs)): return lhs < rhs
case (.fromEnd(let lhs), .fromEnd(let rhs)): return lhs < rhs
}
}
public static func == (_ lhs: OffsetBound, _ rhs: OffsetBound) -> Bool {
switch (lhs.kind, rhs.kind) {
case (.fromStart(_), .fromEnd(_)): return false
case (.fromEnd(_), .fromStart(_)): return false
case (.fromStart(let lhs), .fromStart(let rhs)): return lhs == rhs
case (.fromEnd(let lhs), .fromEnd(let rhs)): return lhs == rhs
}
}
}
// To get a Range<OffsetBound> from a RangeExpression<OffsetBound>
internal struct OffsetBoundConverter<Bound>: Collection {
typealias Element = OffsetBound<Bound>
typealias Index = OffsetBound<Bound>
internal var startIndex: Index { return OffsetBound(fromStart: 0) }
internal var endIndex: Index { return OffsetBound(fromEnd: 0) }
internal func index(after bound: Index) -> Index {
return bound.advanced(by: 1)
}
internal subscript(bound: Index) -> Element { return bound }
internal subscript(bounds: Range<Index>) -> Range<Element> {
return bounds
}
init() { }
}
extension RangeExpression where Bound == OffsetBound {
// TODO: doc
internal func _relative<C: Collection>(to c: C) -> Range<C.Index> {
let range = self.relative(to: OffsetBoundConverter<C.Index>())
let lower = range.lowerBound.relative(to: c)
let upper = range.upperBound.relative(to: c)
return lower ..< max(lower, upper)
}
}
extension Collection {
// TODO: doc
public subscript(offset offset: OffsetBound<Index>) -> Element {
return self[offset.relative(to: self)]
}
public subscript<ORE: RangeExpression>(
offset range: ORE
) -> SubSequence where ORE.Bound == OffsetBound<Index> {
return self[range._relative(to: self)]
}
}
extension MutableCollection {
// TODO: doc
public subscript(offset: OffsetBound<Index>) -> Element {
get {
return self[offset.relative(to: self)]
}
set {
self[offset.relative(to: self)] = newValue
}
}
public subscript<ORE: RangeExpression>(
offset range: ORE
) -> SubSequence where ORE.Bound == OffsetBound<Index> {
get {
return self[range._relative(to: self)]
}
set {
self[range._relative(to: self)] = newValue
}
}
}
// Examples
func printStrings() {
let str = "abcdefghijklmnopqrstuvwxyz"
let idx = str.firstIndex { $0 == "n" }!
print("-- single element subscript --")
print(str[offset: -4]) // w
print(str[..<idx][offset: -100]) // a
print(str[idx...][offset: 1]) // o
// No equivalent: print(str[(idx++1)--10]) // e
print("-- relative range --")
print(str[offset: 1 ..< -2]) // bcdefghijklmnopqrstuvwx
// No equivalent: print(str[..<idx--2 ..< --2]) // lmnopqrstuvwx
print(str[idx...][offset: ..<(-2)]) // nopqrstuvwx
print(str[..<idx][offset: (-2)...]) // lm
// No equivalent: print(str[idx--2..<idx++3]) // lmnop
print(str[offset: -4 ..< -2]) // wx
print("-- relative range through --")
// No equivalent: print(str[idx--2 ... --2]) // lmnopqrstuvwxy
print(str[idx...][offset: ...(-2)]) // nopqrstuvwxy
print(str[...idx][offset: (-3)...]) // lmn
// No equivalent: print(str[idx--2...idx++3]) // lmnopq
print(str[offset: -4 ... -2]) // wxy
print("-- partial relative range up to --")
// No equivalent: print(str[..<idx++2]) // abcdefghijklmno
print(str[..<idx][offset: ..<(-2)]) // abcdefghijk
print(str[offset: ..<20]) // abcdefghijklmnopqrst
print(str[offset: ..<(-20)]) // abcdef
print("-- partial relative range through --")
// No equivalent: print(str[...idx++2]) // abcdefghijklmnop
print(str[...idx][offset: ...(-3)]) // abcdefghijkl
print(str[offset: ...20]) // abcdefghijklmnopqrstu
print(str[offset: ...(-20)]) // abcdefg
print(str[offset: ...(-20)][offset: ...(-3)]) // abcde
// No equivalent: print(str[...((--20)++2)]) // abcdefghi
print("-- partial relative range from --")
print(str[idx...][offset: 2...]) // pqrstuvwxyz
// No equivalent: print(str[idx--2...]) // lmnopqrstuvwxyz
print(str[offset: 20...]) // uvwxyz
print(str[offset: (-20)...]) // ghijklmnopqrstuvwxyz
}
func printSplitFloats() {
func splitAndTruncate<T: BinaryFloatingPoint>(
_ value: T, precision: Int = 3
) -> (whole: Substring, fraction: Substring) {
let str = String(describing: value)
guard let dotIdx = str.firstIndex(of: ".") else { return (str[...], "") }
return (str[..<dotIdx], str[dotIdx...][offset: 1..<OffsetBound(1).advanced(by: precision)])
}
print(splitAndTruncate(1.0)) // (whole: "1", fraction: "0")
print(splitAndTruncate(1.25)) // (whole: "1", fraction: "25")
print(splitAndTruncate(1.1000000000000001)) // (whole: "1", fraction: "1")
print(splitAndTruncate(1.3333333)) // (whole: "1", fraction: "333")
print(splitAndTruncate(200)) // (whole: "200", fraction: "0")
}
func printRanges() {
let r: Range<Int> = 3..<10 // Explicit type only needed for this gist, if part of stdlib, it won't be preferred...
print((absolute: r[5...], relative: r[offset: 5...]))
// (absolute: Range(5..<10), relative: Range(8..<10))
}
func printFifths() {
func getFifth<C: RandomAccessCollection>(
_ c: C
) -> (absolute: C.Element, relative: C.Element) where C.Index == Int {
return (c[5], c[offset: 5])
}
let array = [0, 1,2,3,4,5,6,7,8,9]
print(getFifth(array)) // (absolute: 5, relative: 5)
print(getFifth(array[2...])) // (absolute: 5, relative: 7)
}
import Foundation
func printDataFifths() {
func getFifth(_ data: Data) -> (absolute: UInt8, relative: UInt8) {
return (data[5], data[offset: 5])
}
var data = Data([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(getFifth(data)) // (absolute: 5, relative: 5)
data = data.dropFirst()
print(getFifth(data)) // (absolute: 5, relative: 6)
}
func printRequirements() {
func parseRequirement(
_ str: Substring
) -> (predecessor: Unicode.Scalar, successor: Unicode.Scalar) {
return (str.unicodeScalars[offset: 5], str.unicodeScalars[offset: 36])
}
"""
Step C must be finished before step A can begin.
Step C must be finished before step F can begin.
Step A must be finished before step B can begin.
Step A must be finished before step D can begin.
Step B must be finished before step E can begin.
Step D must be finished before step E can begin.
Step F must be finished before step E can begin.
""".split(separator: "\n").forEach { print(parseRequirement($0)) }
// (predecessor: "C", successor: "A")
// (predecessor: "C", successor: "F")
// (predecessor: "A", successor: "B")
// (predecessor: "A", successor: "D")
// (predecessor: "B", successor: "E")
// (predecessor: "D", successor: "E")
// (predecessor: "F", successor: "E")
}
func runAll() {
printStrings()
printSplitFloats()
printRanges()
printFifths()
printDataFifths()
printRequirements()
}
runAll()
let intRange = 1..<5
dump(intRange) // Range<Int>
let range: ClosedRange<OffsetBound<String.Index>> = 1...(-1)
let x = " Hello, World! "
let y = "abcdefg"
let z = " "
print(x[offset: range]) // "Hello, World!"
print(y[offset: range]) // "bcdef"
print(z[offset: range]) // ""
print("abc"[offset: 1...]) // bc
print("abc"[offset: 1..<(-1)]) // b
print("abc"[offset: 1..<(-2)]) // ""
print("abc"[offset: 1..<(-3)]) // ""
extension BidirectionalCollection {
func element(beforeOffset offset: OffsetBound<Index>) -> Element {
return self[offset: offset.advanced(by: -1)]
}
func element(beforeOffset offset: Int) -> Element {
return self[offset: OffsetBound(fromStart: offset - 1)]
}
}
let array = ["a", "b", "c", "d"]
print(array.element(beforeOffset: OffsetBound(0))) // "a"
print(array.element(beforeOffset: 0)) // "a"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment