Created
July 26, 2016 12:28
-
-
Save beccadax/0946a99528f6e6500d93bbf67684c0b3 to your computer and use it in GitHub Desktop.
Alternate design for RelativeRange
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let array = Array(1...10) | |
array[.startIndex ..< 3] | |
array[.startIndex + 2 ..< .endIndex - 1] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public protocol RangeExpression { | |
/// The type of the bounds of the `Range` produced by this | |
/// type when used as a `RangeExpression`. | |
associatedtype Bound : Comparable | |
/// Returns `self` expressed as a `Range<Bound>` suitable for | |
/// slicing a collection with the indicated properties. | |
/// | |
/// -Parameter bounds: The range of indices in the collection. | |
/// Equivalent to `startIndex ..< endIndex` | |
/// in `Collection`. | |
/// | |
/// -Parameter offset: A function which can be used to add to or | |
/// subtract from a bound. Equivalent to | |
/// `index(_:offsetBy:)` in `Collection`. | |
/// | |
/// -Returns: A `Range<Bound>` suitable for slicing a collection. | |
/// The return value is *not* guaranteed to be inside | |
/// `bounds`. Callers should apply the same preconditions | |
/// to the return value as they would to a range provided | |
/// directly by the user. | |
/// | |
/// -Warning: This method is likely to be replaced in a future version of Swift. | |
/// If you are calling this method, we recommend using the | |
/// `relative(to:)` extension method instead. If you are implementing | |
/// it, be prepared to migrate your code. | |
/// | |
/// -Recommended: `relative(to:)` | |
// | |
// WORKAROUND unfiled - We want to have this requirement, but it triggers a generics bug | |
// func relative<C: Indexable>(to collection: C) -> Range<Bound> where C.Index == Bound | |
func relative<BoundDistance: SignedInteger>(to bounds: Range<Bound>, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Range<Bound> | |
} | |
extension RangeExpression { | |
/// Returns `self` expressed as a range of indices within `collection`. | |
/// | |
/// -Parameter collection: The collection `self` should be | |
/// relative to. | |
/// | |
/// -Returns: A `Range<Bound>` suitable for slicing `collection`. | |
/// The return value is *not* guaranteed to be inside | |
/// its bounds. Callers should apply the same preconditions | |
/// to the return value as they would to a range provided | |
/// directly by the user. | |
/// | |
/// -RecommendedOver: `relative(to:offsettingBy:)` | |
public func relative<C: Indexable>(to collection: C) -> Range<Bound> where C.Index == Bound { | |
let bounds = Range(uncheckedBounds: (lower: collection.startIndex, upper: collection.endIndex)) | |
return relative(to: bounds, offsettingBy: collection.index(_:offsetBy:)) | |
} | |
} | |
enum RelativeBound<Bound: Comparable> { | |
case from(Bound, offset: Int) | |
case fromStart(offset: Int) | |
case fromEnd(offset: Int) | |
static var startIndex: RelativeBound<Bound> { return fromStart(offset: 0) } | |
static var endIndex: RelativeBound<Bound> { return fromEnd(offset: 0) } | |
func destructured(with bounds: Range<Bound>) -> (Bound, Int) { | |
switch self { | |
case let .from(bound, offset): | |
return (bound, offset) | |
case let .fromStart(offset): | |
return (bounds.lowerBound, offset) | |
case let .fromEnd(offset): | |
return (bounds.upperBound, offset) | |
} | |
} | |
func relative<BoundDistance : SignedInteger>(to bounds: Range<Bound>, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Bound { | |
let (bound, offsetDistance) = destructured(with: bounds) | |
return offset(bound, BoundDistance(offsetDistance.toIntMax())) | |
} | |
} | |
func + <Bound: Comparable>(lhs: RelativeBound<Bound>, rhs: Int) -> RelativeBound<Bound> { | |
switch lhs { | |
case .fromEnd(let lhsInt): | |
return .fromEnd(offset: lhsInt + rhs) | |
case .fromStart(let lhsInt): | |
return .fromStart(offset: lhsInt + rhs) | |
case let .from(bound, offset): | |
return .from(bound, offset: offset + rhs) | |
} | |
} | |
func - <Bound: Comparable>(lhs: RelativeBound<Bound>, rhs: Int) -> RelativeBound<Bound> { | |
return lhs + -rhs | |
} | |
struct RelativeRange<Bound: Comparable> { | |
let lowerBound: RelativeBound<Bound> | |
let upperBound: RelativeBound<Bound> | |
} | |
extension RelativeRange: RangeExpression { | |
func relative<BoundDistance : SignedInteger>(to bounds: Range<Bound>, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Range<Bound> { | |
return lowerBound.relative(to: bounds, offsettingBy: offset) ..< upperBound.relative(to: bounds, offsettingBy: offset) | |
} | |
} | |
func ..< <Bound: Comparable>(lhs: RelativeBound<Bound>, rhs: RelativeBound<Bound>) -> RelativeRange<Bound> { | |
return RelativeRange(lowerBound: lhs, upperBound: rhs) | |
} | |
func ..< <Bound: Comparable>(lhs: RelativeBound<Bound>, rhs: Bound) -> RelativeRange<Bound> { | |
return lhs ..< .from(rhs, offset: 0) | |
} | |
func ..< <Bound: Comparable>(lhs: Bound, rhs: RelativeBound<Bound>) -> RelativeRange<Bound> { | |
return .from(lhs, offset: 0) ..< rhs | |
} | |
extension Indexable { | |
subscript(bounds: RelativeRange<Index>) -> SubSequence { | |
return self[bounds.relative(to: self)] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment