Add Stride for Collection#24
Conversation
6819ce7 to
18bd60d
Compare
| /// - Parameter step: The amount to step with each iteration. | ||
| /// - Returns: Returns a collection stepping through the elements by the | ||
| /// specified amount. | ||
| public func stride(by step: Int) -> Stride<Self> { |
There was a problem hiding this comment.
I wonder if this would best be striding to fit the ed/ing rule for naming nonmutating members. That said, I also worry about ambiguity with the existing StrideTo/StrideThrough types: are there other terms that are often used for this operation?
There was a problem hiding this comment.
I am in agreement that striding is probably a better fit for this name for the reasons you've mentioned. I'm a little unsure that renaming the type to something else would be a good idea as the notion of a stride is widely known and something which is obvious as soon as you see it. Other terms used for striding include increment, pitch or step - whilst they offer a similar meaning, they do not have the same understanding as striding. In one of my earlier implementations I had named the type StridingCollection which I would not be against being explicit about, I removed the suffix Collection to keep in step with the rest of the algorithms in this repository. It would be good to see what other feedback I get regarding the naming of this before I change it. Thanks for your thoughts @xwu
natecook1000
left a comment
There was a problem hiding this comment.
This is looking good, @ollieatkinson — thanks for this addition!
natecook1000
left a comment
There was a problem hiding this comment.
@ollieatkinson A few more notes for you!
|
|
||
| public struct Stride<Base: Collection> { | ||
|
|
||
| public let base: Base |
There was a problem hiding this comment.
To help simplify the issue, do we need to expose base at all?
There was a problem hiding this comment.
I was also unsure of the value of being able to refer to base in this instance, but since all of the other collection algorithms do I thought it would be worth keeping consistent. Was there a specific issue you think that it might simplify?
There was a problem hiding this comment.
Sorry, the specific issue is the question of equality, which @natecook1000 mentioned above.
Two values are equal when they behave the same way for all salient operations. For this purpose, we say that iteration order isn’t salient for sets but is salient for arrays, that the sign of zero isn’t salient for floating-point numbers, etc.
It’s a judgment call about what is salient here, and reasonable people can disagree whether differing base elements would matter; by contrast, if base isn’t recoverable, then there is no ambiguity at all.
There was a problem hiding this comment.
I like what you're saying here and I'd be up for dropping the access modifier from the property - I cannot fathom a situation where you would want to use it outside of the scope of the collection itself.
There was a problem hiding this comment.
Agreed on this — base can be internal, which removes any controversy here. (That should probably be the case for the rest of the collection wrappers as well.)
There was a problem hiding this comment.
Thanks both for your thoughts, I've removed public from base here and we can follow up in another P/R for the rest of the collections
There was a problem hiding this comment.
by contrast, if
baseisn’t recoverable, then there is no ambiguity at all.
I don't see how this matters in case the user knows what base is, even if Stride doesn't expose it. Couldn't there still be controversy about whether (0...10).striding(by: 4) should equal (0...11).striding(by: 4)?
natecook1000
left a comment
There was a problem hiding this comment.
I think just these last couple of notes, then this will be ready 👍
| } | ||
|
|
||
| public func index(_ i: Index, offsetBy distance: Int) -> Index { | ||
| precondition(i.base < base.endIndex, "Advancing past end index") |
There was a problem hiding this comment.
This isn't a valid precondition, since c.index(c.endIndex, offsetBy: -1) is valid for bidirectional collections.
afbb684 to
74a25e8
Compare
…lities for validateIndexTraversals
74a25e8 to
6df6133
Compare
|
I've rebased against the latest |
|
|
That makes sense, I've added a commit to address this behaviour here |
f8461f5 to
3669ff0
Compare
|
@natecook1000 Is there any more work required on this for it to be merged? |
| limitedBy limit: Index | ||
| ) -> Index? { | ||
| let distance = i == endIndex | ||
| ? -((base.count - 1) % stride + 1) + (n - 1) * -stride |
There was a problem hiding this comment.
FYI, Xcode 12.2 fails to compile this line due to an "expression too complex" error. Making the 1 values Int(1) seems to resolve the issue.
There was a problem hiding this comment.
Thanks for raising this - I'm surprised that it's too complex given these are all integers, I could also annotate the distance property : Int to have the same effect.
I have just tested this on Big Sur 11.0.1 and Xcode 12.2 and I was able to compile fine - do you have anything else in your environment which might cause this, so I can track it down?
|
Thanks, @ollieatkinson — this is ready to go! |
Many thanks to @timvermeulen for continued help!
Stride
A type that steps over a collection’s elements by the specified amount.
This is available through the
striding(by:)method on anyCollection.If the stride is larger than the collection count, the resulting wrapper only contains the
first element.
The stride amount must be a positive value.
Detailed Design
The
striding(by:)method is declared as aCollectionextension, and returns aStridetype:A custom
Indextype is defined so that it's not possible to get confused when tryingto access an index of the stride collection.
A careful thought was given to the composition of these strides by giving a custom
implementation to
index(_:offsetBy:limitedBy)which multiplies the offset by thestride amount.
The following two lines of code are equivalent, including performance:
Complexity
The call to
striding(by: k)is always O(1) and access to the next value in the strideis O(1) if the collection conforms to
RandomAccessCollection, otherwise O(k).Comparison with other languages
rust has
Stridedavailable in a crate.c++ has std::slice::stride
The semantics of
stridingdescribed in this documentation are equivalent.Checklist