# Stride Operators, (a..<b)/step and (a...b)/step

I propose use of stride operators to easily create StrideTo and StrideThrough instances.

The definition of stride operators:

For example,

(-10…<10)/4
is equivalent to
stride(from: -10, to: 10, by: 4)

Array((-10…<10)/4)
gives
[-10, -6, -2, 2, 6]

(-10…10)/4
is equivalent to
stride(from: -10, through: 10, by: 4)

Array((-10…10)/4)
gives
[-10, -6, -2, 2, 6, 10]

3 Likes

Cool. Adding a stride operator of some sort is interesting. The / operator’s precedence is too tight though. Also, I see what you’re going for by re-using /, but it seems like striding is a bit different. Given that strides work on all sequences (not just numeric sequences) so it could be misleading. I’d suggest exploring the idea of using a new operator, dedicated to striding, with its own unique spelling.

-Chris

3 Likes

``````precedencegroup StridePrecedenceGroup {
lowerThan: RangeFormationPrecedence
}

infix operator ++ : StridePrecedenceGroup
infix operator -- : StridePrecedenceGroup

// Open Range Stride
func ++ <T: Strideable>(left: Range<T>, right: T.Stride) -> StrideTo<T> {
return stride(from: left.lowerBound, to: left.upperBound, by: right)
}

// Closed Range Stride
func ++ <T: Strideable>(left: ClosedRange<T>, right: T.Stride) -> StrideThrough<T> {
return stride(from: left.lowerBound, through: left.upperBound, by: right)
}

// Open Range Stride
func -- <T: Strideable>(left: Range<T>, right: T.Stride) -> StrideTo<T> {
return stride(from: left.upperBound, to: left.lowerBound, by: -right)
}

// Closed Range Stride
func -- <T: Strideable>(left: ClosedRange<T>, right: T.Stride) -> StrideThrough<T> {
return stride(from: left.upperBound, through: left.lowerBound, by: -right)
}

Array(-10 ... 10 ++ 4) // [-10, -6, -2, 2, 6, 10]
Array(-10 ... 10 -- 3) // [10, 7, 4, 1, -2, -5, -8]
``````

Back with a vengeance

Edit: the `--` is included mostly for symmetry, the logic is completely broken and doesn’t respect the boundaries of the ranges. Will be worth thinking about if both a stride operator and a decreasing range make it to the language.

2 Likes

Cute. Is there a need for two stride operators though?

I don’t think there is a need for both today.

I worry about deriving strides from ranges. It would preclude using whatever `Range` operator is agreed upon to use a negative step to stride from an upper bound to (as opposed to through) a lower bound, as half-open ranges closed at their upper bound don’t exist. While Swift doesn’t have bottom-open-top-closed ranges, it is perfectly symmetrical regarding whether StrideTo may omit the larger or smaller of its endpoints – it always omits the destination endpoint. I don’t think the asymmetry of `Range` should carry over to `StrideTo`.

Perhaps a different syntax altogether might make sense? Something like `from..by..<to` and `from..by...through`. (Surely this has been discussed before…?)

Good Suggestion!
I confirmed that `(from..by)..<to` and `(from..by)...through` can be implemented as follows:

``````struct FromByDescriptor<T: Strideable> {
var from: T
var by: T.Stride
}

infix operator ..

func .. <T: Strideable>(left: T, right: T.Stride) -> FromByDescriptor<T> {
return FromByDescriptor(from: left, by: right)
}

func ..< <T: Strideable>(left: FromByDescriptor<T>, right: T) -> StrideTo<T> {
return stride(from: left.from, to: right, by: left.by)
}

func ... <T: Strideable>(left: FromByDescriptor<T>, right: T) -> StrideThrough<T> {
return stride(from: left.from, through: right, by: left.by)
}
``````

I agree that starting from a range is not ideal. But I’m also not sure that `a..b..<c` is better than `stride(from: a, to: c, by: b)`. This is especially the case with `10 .. -4 ..< -10`, which doesn’t strike me as intuitive at all.

I think a better solution is to add the other handedness of half open range. `-10 <.. 10 -- 4` is also not great, but at least the comparison operator is the right way round.

In an ideal world, ranges wouldn’t be iterable at all, but that’s a whole other discussion.

By the way, Swift has the ternary conditional operator (a ? b : c). Why does Swift not have ternary stride operators (e.g. `from .. by ..< to` and `from .. by ... through`)?

I understood the implementation of the ternary stride operators needs a new `precedencegroup` as the following:

``````precedencegroup StridePrecedence {
higherThan: RangeFormationPrecedence
}

infix operator ..: StridePrecedence
``````

Is that correct?

For instance, the implementation for `from ..< to .. by` and `from ... through .. by` is as follows:

``````struct UpperBoundStepDescriptor<T: Strideable> {
var upperBound: T
var step: T.Stride
}

precedencegroup StridePrecedence {
higherThan: RangeFormationPrecedence
}

infix operator ..: StridePrecedence

func .. <T: Strideable>(left: T, right: T.Stride) -> UpperBoundStepDescriptor<T> {
return UpperBoundStepDescriptor(upperBound: left, step: right)
}

func ..< <T: Strideable>(left: T, right: UpperBoundStepDescriptor<T>) -> StrideTo<T> {
return stride(from: left, to: right.upperBound, by: right.step)
}

func ... <T: Strideable>(left: T, right: UpperBoundStepDescriptor<T>) -> StrideThrough<T> {
return stride(from: left, through: right.upperBound, by: right.step)
}
``````

Swift has `reversed()`. So, `(-6 .. 4 ... 10).reversed()` or `(-6 ... 10 .. 4).reversed()` can replace `10 .. -4 ..< -10` or `-10 <.. 10 -- 4`.

Edit: I am sorry. We don’t need `reversed()` for `stride`:
`Array(10 .. -4 ... -9) // [10, 6, 2, -2, -6]`
or
`Array(10 ... -9 .. -4) // [10, 6, 2, -2, -6]`

For `stride`, `..!` might replace `..<`.

``````infix operator ..!: RangeFormationPrecedence

func ..! <T: Strideable>(left: T, right: UpperBoundStepDescriptor<T>) -> StrideTo<T> {
return stride(from: left, to: right.upperBound, by: right.step)
}
``````

For example,

``````Array(10 ... -9 .. -4) // [10, 6, 2, -2, -6]
Array(10 ..! -10 .. -4) // [10, 6, 2, -2, -6]
``````

I’m really not a fan of the ternary operator, and I wish it wasn’t in the language.

When deciding on an approach for stride, I think that we need something that is easy to understand the first time you see it, and not too cluttered.

I think that what we actually want is a new `Stride` type with no bounds, one that can be converted to one with bounds with an operator or a method.

``````struct StrideIterator<Element : Strideable> {
var element: Element
let step: Element.Stride
}

extension StrideIterator : IteratorProtocol where Element : Strideable {

mutating func next() -> Element? {
defer { element = element.advanced(by: step) }
return element
}
}

struct Stride<Element : Strideable> {
var start: Element
var step: Element.Stride
}

extension Stride : Sequence {

typealias Iterator = StrideIterator<Element>

func makeIterator() -> Iterator {
return StrideIterator(element: start, step: step)
}
}

extension Stride {

func stop(before end: Element) -> StrideTo<Element> {
return stride(from: start, to: end, by: step)
}

func stop(on end: Element) -> StrideThrough<Element> {
return stride(from: start, through: end, by: step)
}
}

func stride<Element: Strideable>(from start: Element, by step: Element.Stride) -> Stride<Element> {
return Stride(start: start, step: step)
}

precedencegroup StrideStopPrecedenceGroup {
}

precedencegroup StridePrecedenceGroup {
higherThan: StrideStopPrecedenceGroup
}

infix operator ++ : StridePrecedenceGroup

infix operator ..! : StrideStopPrecedenceGroup
infix operator ... : StrideStopPrecedenceGroup

func ++ <T: Strideable>(start: T, step: T.Stride) -> Stride<T> {
return stride(from: start, by: step)
}

func ..! <T: Strideable>(stride: Stride<T>, stop: T) -> StrideTo<T> {
return stride.stop(before: stop)
}

func ... <T: Strideable>(stride: Stride<T>, stop: T) -> StrideThrough<T> {
return stride.stop(on: stop)
}

for x in -10 ++ 4 {
print(x)
break
}

for x in -10 ++ 4 ..! 10 {
print(x)
}

print(Array((10 ++ -4).stop(before: -10)))
print(Array(-10 ++ 4 ..! 10))
``````

Output:

``````-10
-10
-6
-2
2
6
[10, 6, 2, -2, -6]
[-10, -6, -2, 2, 6]
``````

Nice idea! But, the `++` operator would mislead us because we well know the deprecated `++` operator. `..+` or `.++` might be preferred because it is similar to `...`, `..<`, and `..!`.

``````for x in -10 ..+ 4 {
print(x)
break
}
Array(10 ..+ -4 ... -10)
Array(-10 ..+ 4 ..! 10)
``````

Also whatever syntax is decided upon should be usable with `CountablePartialRangeFrom` as well for variable stepping with no upper bound. It’s actually somewhat surprising that there is no such thing as `stride(from:_, by:_)`.

1 Like

I noticed no subscript of `Array` for `stride`.

``````var x = Array(0..<10)
x[stride(from: 1, through: 8, by: 2)] = [-1, -2, -3]
// should be [0, -1, 2, -2, 4, -3, 6, 7, 8, 9]
``````

gives error messages:

``````error: repl.swift:2:2: error: cannot subscript a value of type '[Int]' with an index of type 'StrideThrough<Int>'
x[stride(from: 1, through: 8, by: 2)] = [-1, -2, -3]
^

repl.swift:2:2: note: overloads for 'subscript' exist with these partially matching parameter lists: (Int), (Range<Int>), (Range<Self.Index>), ((UnboundedRange_) -> ())
x[stride(from: 1, through: 8, by: 2)] = [-1, -2, -3]
^
``````

First, I made minor modification to Alexandre’s implementation as the following:

``````precedencegroup StrideStopPrecedence {
higherThan: RangeFormationPrecedence
}

precedencegroup StrideFormationPrecedence {
higherThan: StrideStopPrecedence
}

infix operator ..+ : StrideFormationPrecedence

infix operator ..! : StrideStopPrecedence
infix operator ... : StrideStopPrecedence

func ..+ <T: Strideable>(start: T, step: T.Stride) -> Stride<T> {
return stride(from: start, by: step)
}
``````

Then, I added an extension of `Array` to subscript `Array` with `stride`:

``````extension Array  {
subscript (stride: Stride<Int>) -> Array<Element> {
get {
let stride = stride.stop(before: self.count)
return stride.map { self[\$0] }
}
set (newValues) {
let stride = stride.stop(before: self.count)
zip(stride, newValues).forEach { self[\$0] = \$1 }
}
}
subscript (stride: StrideTo<Int>) -> Array<Element> {
get {
return stride.map { self[\$0] }
}
set (newValues) {
zip(stride, newValues).forEach { self[\$0] = \$1 }
}
}
subscript (stride: StrideThrough<Int>) -> Array<Element> {
get {
return stride.map { self[\$0] }
}
set (newValues) {
zip(stride, newValues).forEach { self[\$0] = \$1 }
}
}
}
``````

For example,

``````var x = Array(0..<10)
x[stride(from: 1, by: 2)]
x[1..+2]
// [1, 3, 5, 7, 9]

x[stride(from: 1, to: 8, by: 2)]
x[1..+2..!8]
// [1, 3, 5, 7]

x[stride(from: 1, through: 8, by: 2)]
x[1..+2...8]
// [1, 3, 5, 7]

x = Array(0..<10)
x[1..+2] = [-1, -2, -3]
// [0, -1, 2, -2, 4, -3, 6, 7, 8, 9]
x[1..+2] = [-1, -2, -3, -4]
// [0, -1, 2, -2, 4, -3, 6, -4, 8, 9]
x[1..+2] = [-1, -2, -3, -4, -5]
// [0, -1, 2, -2, 4, -3, 6, -4, 8, -5]

x = Array(0..<10)
x[1..+2..!8] = [-1, -2, -3]
// [0, -1, 2, -2, 4, -3, 6, 7, 8, 9]
x[1..+2..!8] = [-1, -2, -3, -4]
// [0, -1, 2, -2, 4, -3, 6, -4, 8, 9]
x[1..+2..!8] = [-1, -2, -3, -4, -5]
// [0, -1, 2, -2, 4, -3, 6, -4, 8, 9]

x = Array(0..<10)
x[1..+2...8] = [-1, -2, -3]
// [0, -1, 2, -2, 4, -3, 6, 7, 8, 9]
x[1..+2...8] = [-1, -2, -3, -4]
// [0, -1, 2, -2, 4, -3, 6, -4, 8, 9]
x[1..+2...8] = [-1, -2, -3, -4, -5]
// [0, -1, 2, -2, 4, -3, 6, -4, 8, 9]
``````

Is there another implementation for the subscript?

Maybe, we need a new generic structure like an `ArraySlice` with a stride for
`subscript (stride:) -> ArrayStrideSlice { get }`.

Maybe, we need a `Slice` structure with a `stride`, which might be named as `StrideSlice`.

Here is the definition of the `Slice` structure from `swift/stdlib/public/core/Slice.swift`.

``````public struct Slice<Base: Collection> {
public var _startIndex: Base.Index
public var _endIndex: Base.Index
internal var _base: Base
public init(base: Base, bounds: Range<Base.Index>) {
self._base = base
self._startIndex = bounds.lowerBound
self._endIndex = bounds.upperBound
}
public var base: Base {
return _base
}
}
``````

The `Index` of `Collection` is `Comparable`.

``````public protocol Collection: Sequence where SubSequence: Collection {
associatedtype Index : Comparable
}
``````

So, `StrideSlice` might be defined as:

``````public struct StrideSlice <Base: Collection> {
public var _startIndex: Base.Index
public var _endIndex: Base.Index
public var _stride: Base.Index.Stride
internal var _base: Base
public init(base: Base, strideTo: StrideTo<Base.Index>) {
self._base = base
self._startIndex = strideTo.start
self._endIndex = strideTo.end
self._stride = strideTo.stride
}
public var base: Base {
return _base
}
}
``````

The `Index` of `Collection` should be `Strideable`.

``````public protocol Collection: Sequence where SubSequence: Collection {
associatedtype Index : Strideable
}
``````

FWIW `StrideTo` and `StrideThrough` are both `Sequence`'s, so the subscript functionality could be done by generalising this functionality onto a `Collection` taking a `Sequence`.

``````extension Collection {
// should have some specialised return type instead of Array<Element>
subscript<S: Sequence>(seq: S) -> [Element]
where S.Element == Index {
var result = [Element]()
for i in seq {
result.append(self[i])
}
return Array(result)
}
}

let x = Array(1...10)

print(x[stride(from: 0, to: 10, by: 2)])
print(x[sequence(first: 0) { \$0 + 2 >= 10 ? nil : \$0 + 2 } ])
``````

This has the obvious added benefit of working with any sequence as a subscript, though I’m not sure why Swift doesn’t have this already though. I can’t see many downsides to having this, but I’m sure someone will point them out

1 Like