-
Notifications
You must be signed in to change notification settings - Fork 448
Unify chunked(by:) and chunked(on:) #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These seem like great improvements!
It makes sense to me to be more frugal about calling the projection closure in case, say, there's a type conversion that heap allocates in there.
It also strikes me as unusual that chunked[chunked.endIndex]
didn't trap before. I think this new approach is more consistent with how the other collections in the stdlib behave. I consider this a bigger benefit than chunked.index(after: chunked.endIndex)
trapping, which I agree is nice to have as long as performance isn't impacted.
As for the open questions, I think I agree with the decisions you made:
LazyChunked.Index could store the projected value.
I think keeping the Index lightweight is more consistent with the stdlib. I believe this is preferable so as to mitigate incurring referencing counting overhead.
Should chunked(on:by:) be public as well?
My inclination is to be cautious here and limit the API surface area. What do you see as the biggest benefits for adding it?
Yep, indeed just nice to have, and in this case a natural consequence of organizing the code better (making
That's a great point! This makes me quite confident that this is the right choice, especially knowing how few closure calls it would prevent in the first place.
It's a tough question. My initial thought was that I wouldn't want people to have to hand-roll this type if they need the Let's err on the side of keeping the API surface area small, we can always add it later if there's a compellinig use case. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very cool, @timvermeulen! Just one note below, then I think this is ready to merge.
let upperBound = i.upperBound ?? endOfChunk(from: i.lowerBound) | ||
let end = endOfChunk(from: upperBound) | ||
let upperBound = i.upperBound ?? endOfChunk(startingAt: i.lowerBound) | ||
guard upperBound != base.endIndex else { return endIndex } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it still has the Ah, I see it. I think I'd rather be explicit to trap here with an c.index(after: c.endIndex) == c.endIndex
behavior — let's have it trap on that case instead.index(after:)
-specific message than rely on the subscripting in endOfChunk(from:)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds reasonable!
4c0037c
to
2976fa6
Compare
Unify
chunked(by:)
andchunked(on:)
by:chunked(on:by:)
, andLazyChunked
store both a projection function and a predicate.The status quo of
chunked(on:)
callingchunked(by: { f($0) == f($1) })
causes more calls to the projection function than necessary, specifically by repeatedly calling it with the first element of the chunk. Unifyingchunked(by:)
andchunked(on:)
allows this projected value to be stored and reused.This PR also slightly improves
endOfChunk(startingAt:)
by moving theendIndex
check toindex(after:)
(at the only point where it can ever be hit). This change, together with changing theendIndex
representation to have no upper bound, has the pleasant consequence thatchunked[chunked.endIndex]
andchunked.index(after: chunked.endIndex)
now both trap rather than returning an empty slice andendIndex
, respectively.Open questions:
LazyChunked.Index
could store the projected value. This saves a single call to the projection function for each chunk, but forchunked(by:)
it only increases the size of the index without any benefit. This does not seem like a clear win so I chose to keepIndex
lean for now.chunked(on:by:)
be public as well? I've left the public API unchanged but I think exposing it might be the nice thing to do.Checklist