-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Fix Enumerable#{zip,zip?} when self is an Iterator (#9328) #9330
Fix Enumerable#{zip,zip?} when self is an Iterator (#9328) #9330
Conversation
Both methods call `size` and as such consume the Iterator (`self`). Then they call Enumerable.zip which tries to again iterate over the now empty Iterator resulting in an empty array. Running std_spec "before" removing the call to `size` results in the following failures: Failures: 1) Enumerable zip works for Iterators as receiver Failure/Error: SpecCountUpIterator.new(3).zip(1..3, 2..4).should eq([{0, 1, 2}, {1, 2, 3}, {2, 3, 4}]) Expected: [{0, 1, 2}, {1, 2, 3}, {2, 3, 4}] got: [] # spec/std/enumerable_spec.cr:1057 2) Enumerable zip? works for Iterators as receiver Failure/Error: SpecCountUpIterator.new(3).zip?(1..2, 2..4).should eq([{0, 1, 2}, {1, 2, 3}, {2, nil, 4}]) Expected: [{0, 1, 2}, {1, 2, 3}, {2, nil, 4}] got: [] # spec/std/enumerable_spec.cr:1063 Fixes: #9328
Shouldn't we try to preserve the optimization for |
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.
Looks great, thank you!
There are no changes to Indexable in this PR. |
src/enumerable.cr
Outdated
@@ -1634,7 +1634,7 @@ module Enumerable(T) | |||
# a.zip(b, c) # => [{1, 4, 8}, {2, 5, 7}, {3, 6, 6}] | |||
# ``` | |||
def zip(*others : Indexable | Iterable | Iterator) | |||
pairs = Array(typeof(zip(*others) { |e| break e }.not_nil!)).new(size) | |||
pairs = Array(typeof(zip(*others) { |e| break e }.not_nil!)).new |
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.
I mean if other
is an Indexable
, we could use its size
?
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's not correct, the size is always taken from self
.
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.
Speficially, [1, 2, 3].zip(4..5)
should give [[1, 4], [2, 5]]
. Even if we take the size from the other indexable, it might not be enough and there will be a reallocation.
In short, maybe it can be done, but I don't know. Better to be safe.
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.
@asterite: [1,2,3].zip(4..5)
raises an IndexError
as written in the documentation. All enumerable must have at least as many elements as self
. So the size of the returned Array is always exactly self.size
or an exception is thrown.
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.
I think it'd be nice to keep this optimization in for Indexable
. self.is_a?(Indexable) ? size : 0
should work well.
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.
@RX14: thanks. I did exactly that!
It's certainly not a huge deal to loose the size hint for the array allocation. But it would still be a nice to use that for |
@straight-shoota One could add Indexable#zip and #zip? as shown below. Note that they both use
A more general solution would be to add a method Rust has this: Further adding |
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 is fine for now, if you want to follow up with an optimized version for Indexable
, that would be much appreciated!
Both methods call
size
and as such consume the Iterator (self
). Then theycall Enumerable.zip which tries to again iterate over the now empty Iterator
resulting in an empty array.
Running std_spec "before" removing the call to
size
results in the followingfailures:
Fixes: #9328