Created
July 26, 2016 10:22
-
-
Save beccadax/3c5c64d3b7ca3ff6b68f1c86163c39c4 to your computer and use it in GitHub Desktop.
Experimenting with the $-based relative range syntax
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[$ ..< 3] | |
array[$ + 2 ..< $ - 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:)) | |
} | |
} | |
struct RelativeBound { | |
var offset: Int | |
} | |
let $ = RelativeBound(offset: 0) | |
func + (lhs: RelativeBound, rhs: Int) -> RelativeBound { | |
return RelativeBound(offset: lhs.offset + rhs) | |
} | |
func - (lhs: RelativeBound, rhs: Int) -> RelativeBound { | |
return RelativeBound(offset: lhs.offset - rhs) | |
} | |
enum RelativeOrAbsolute<Bound> { | |
case absolute(Bound) | |
case relative(RelativeBound) | |
func boundRelative<BoundDistance : SignedInteger>(to bound: Bound, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Bound { | |
switch self { | |
case .absolute(let bound): | |
return bound | |
case .relative(let relativeBound): | |
return offset(bound, BoundDistance(relativeBound.offset.toIntMax())) | |
} | |
} | |
} | |
struct RelativeRange<Bound: Comparable> { | |
let lowerBound: RelativeOrAbsolute<Bound> | |
let upperBound: RelativeOrAbsolute<Bound> | |
} | |
extension RelativeRange: RangeExpression { | |
func relative<BoundDistance : SignedInteger>(to bounds: Range<Bound>, offsettingBy offset: (Bound, BoundDistance) -> Bound) -> Range<Bound> { | |
return lowerBound.boundRelative(to: bounds.lowerBound, offsettingBy: offset) ..< upperBound.boundRelative(to: bounds.upperBound, offsettingBy: offset) | |
} | |
} | |
func ..< <Bound: Comparable>(lhs: RelativeOrAbsolute<Bound>, rhs: RelativeOrAbsolute<Bound>) -> RelativeRange<Bound> { | |
return RelativeRange(lowerBound: lhs, upperBound: rhs) | |
} | |
func ..< <Bound: Comparable>(lhs: RelativeBound, rhs: Bound) -> RelativeRange<Bound> { | |
return .relative(lhs) ..< .absolute(rhs) | |
} | |
func ..< <Bound: Comparable>(lhs: Bound, rhs: RelativeBound) -> RelativeRange<Bound> { | |
return .absolute(lhs) ..< .relative(rhs) | |
} | |
func ..< <Bound: Comparable>(lhs: RelativeBound, rhs: RelativeBound) -> RelativeRange<Bound> { | |
return .relative(lhs) ..< .relative(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