Skip to content

Instantly share code, notes, and snippets.

@beccadax
Created July 26, 2016 10:22
Show Gist options
  • Save beccadax/3c5c64d3b7ca3ff6b68f1c86163c39c4 to your computer and use it in GitHub Desktop.
Save beccadax/3c5c64d3b7ca3ff6b68f1c86163c39c4 to your computer and use it in GitHub Desktop.
Experimenting with the $-based relative range syntax
let array = Array(1...10)
array[$ ..< 3]
array[$ + 2 ..< $ - 1]
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