Implement interleave()#405
Conversation
|
I'm not sure why this fails, some of the errors are that |
|
It's not you, but futures update -- see #412. At a glance, the PR looks great. I'll try to give it a proper review soon! |
|
Ok, thank you :-) Let me know if there's anything I can do... |
|
Can you now please rebase, to see how CI fares? |
|
Sure thing |
For parity with itertools, interleave() should alternately produce elements from two given iterators. Once one of the iterators run out, elements should just be drawn from the other until both are exhausted. This is from #384
Remove unused import, remove commented test code and add more test cases
|
Sorry about the noise, I rebased on a different machine, and forgot to set |
This feature wasn't present in Rust 1.12.
|
@cuviper It should build now, I installed a rust 1.12.0 locally. The commitment to backwards compatibility is pretty cool, I think. 🤞 for tests passing |
|
Looks like the order is getting mixed up: |
|
Yeah, I'm not sure why though - I can't get it to fail locally, and it seems to be only on nightly that it fails :-/ |
nightly is the only one that runs tests, if only because the dev-dependency |
|
Hmm, that makes sense - am I doing something wrong then? I run |
|
I think Travis runs with 2-cpus, so setting |
|
Oh, that did it, thanks! I'll do some debugging now :-) |
The flag indicating the next iterator to pull from was almost carried through all the way, except for InterleaveSeq's constructor, which always set it to false.
|
Thanks for the help @cuviper! I had forgotten to carry the flag through to InterleaveSeq's constructor. |
src/iter/interleave.rs
Outdated
| { | ||
| i: I, | ||
| j: J, | ||
| flag: bool, |
There was a problem hiding this comment.
Do we really need the flag here? I think it's not used until we get to the Producer.
src/iter/interleave.rs
Outdated
| j: J, | ||
| i_len: usize, | ||
| j_len: usize, | ||
| flag: bool, |
There was a problem hiding this comment.
I think we need a more informative name for this flag, and a doc-comment explaining its purpose as well. Think of the poor developer who may have to revisit this code many months later... :)
There was a problem hiding this comment.
I'll try to think of a better name... names are hard :-)
There was a problem hiding this comment.
I'm open for suggestions on the name :-) Any of these you'd prefer?
toggle_flagnext_iter/next_producer
There was a problem hiding this comment.
It's indicating which iterator will produce the next item, so maybe i_next or j_next? (depending on what you want true to mean)
| self.flag = !self.flag; | ||
| if self.flag { | ||
| match self.i.next() { | ||
| None => self.j.next(), |
There was a problem hiding this comment.
This sort of assumes that i will be fused (continue returning None), as the flag flips back and forth trying this iterator again. We may want a done: bool to remember and stop flipping the flag, or I guess len() can tell us this too.
There was a problem hiding this comment.
Yes, the itertools version is fused. I'm not really sure of the best way to handle it, I would just add a done bool I think
There was a problem hiding this comment.
Ah, it's literally fused! You could just use that too. Fuse has a done bool itself, but it also specializes on the unstable FusedIterator trait to skip that check for naturally-fused iterators.
There was a problem hiding this comment.
Ok, I added Fuse to both iterators in InterleaveSeq
src/iter/interleave.rs
Outdated
| pub struct InterleaveSeq<I, J> { | ||
| i: I, | ||
| j: J, | ||
| flag: bool |
There was a problem hiding this comment.
Again, better name and comments please.
src/iter/interleave.rs
Outdated
|
|
||
| fn size_hint(&self) -> (usize, Option<usize>) { | ||
| let (ih, jh) = (self.i.size_hint(), self.j.size_hint()); | ||
| let min = ih.0.checked_add(jh.0).unwrap_or(usize::MAX); |
There was a problem hiding this comment.
Perhaps saturating_add instead?
src/iter/interleave.rs
Outdated
| { | ||
| #[inline] | ||
| fn next_back(&mut self) -> Option<I::Item> { | ||
| if self.i.len() <= self.j.len() { |
There was a problem hiding this comment.
I think if they are equal length, then it needs to take the flag into account.
src/iter/mod.rs
Outdated
| zip::new(self, zip_op.into_par_iter()) | ||
| } | ||
|
|
||
| /// Interleave elements of this iterator and the other given iterator. |
There was a problem hiding this comment.
Your PR is more descriptive than these docs -- please elaborate! An example would be nice too.
| xs.par_iter().interleave(&ys).map(|&i| i).collect_into(&mut res); | ||
| assert_eq!(expected, res, "Case {} failed", i+1); | ||
| } | ||
| } |
There was a problem hiding this comment.
How about a test under rev(), for the next_back behavior?
There was a problem hiding this comment.
I added another assert_eq that tests each case when using rev()
Documentation in a few places, add tests with `rev()` to exercise `next_back()` and fix the `next_back()` implementation.
- Remove ExactSizeIterator from Iterator impl for InterleaveSeq - Use `saturating_add()` instead of `checked_add().unwrap_or(MAX)`
- Rename `flag` to `i_next` - Use `Fuse` for the iterators embedded in `InterleaveSeq`
|
Let's merge -- thanks! |
|
Thanks! It should be relatively easy to implement |
|
Yeah, I think |
|
Hmm, it may not be as trivial as I thought - the But by their example, it is not simply (source) Observe that the last element is drawn from the first iterator, ie three elements are drawn from that one while the second only provides two elements. I can see how I'd change |
|
Something like this? if i.len() <= j.len() {
// take equal lengths from both iterators
let n = i.len();
i.take(n).interleave(j.take(n))
} else {
// take one extra item from the first iterator
let n = j.len();
i.take(n + 1).interleave(j.take(n))
}(using |
|
Thanks for the suggestion! I was trying something similar, but run into: |
I think we'll want this to return a distinct |
For parity with itertools, interleave() should alternately produce
elements from two given iterators. Once one of the iterators run out,
elements should just be drawn from the other until both are exhausted.
This is from #384