-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Add Interleave iterator #15886
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
Add Interleave iterator #15886
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,7 +68,7 @@ use clone::Clone; | |
use cmp; | ||
use cmp::{PartialEq, PartialOrd, Ord}; | ||
use mem; | ||
use num::{Zero, One, CheckedAdd, CheckedSub, Saturating, ToPrimitive, Int}; | ||
use num::{Zero, One, CheckedAdd, CheckedSub, CheckedMul, Saturating, ToPrimitive, Int}; | ||
use ops::{Add, Mul, Sub}; | ||
use option::{Option, Some, None}; | ||
use uint; | ||
|
@@ -144,6 +144,25 @@ pub trait Iterator<A> { | |
Zip{a: self, b: other} | ||
} | ||
|
||
/// Creates an iterator which interleaves the contents of this and the specified | ||
/// iterator. | ||
/// | ||
/// # Example | ||
/// | ||
/// ```rust | ||
/// let a = [0i, 2i]; | ||
/// let b = [1i]; | ||
/// let mut it = a.iter().interleave(b.iter()); | ||
/// assert_eq!(it.next().unwrap(), &0i); | ||
/// assert_eq!(it.next().unwrap(), &1i); | ||
/// assert_eq!(it.next().unwrap(), &1i); | ||
/// assert!(it.next().is_none()); | ||
/// ``` | ||
#[inline] | ||
fn interleave<U: Iterator<A>>(self, other: U) -> Interleave<Self, U> { | ||
Interleave{a: self, b: other, using_first: true} | ||
} | ||
|
||
/// Creates a new iterator which will apply the specified function to each | ||
/// element returned by the first, yielding the mapped element instead. | ||
/// | ||
|
@@ -1241,6 +1260,112 @@ RandomAccessIterator<(A, B)> for Zip<T, U> { | |
} | ||
} | ||
|
||
/// An iterator which interleaves the contents of two other iterators | ||
#[deriving(Clone)] | ||
pub struct Interleave<T, U> { | ||
a: T, | ||
b: U, | ||
using_first: bool | ||
} | ||
|
||
fn interleave_len(a_len: uint, b_len: uint, using_first: bool) -> Option<uint> { | ||
let off_by_one = using_first as uint; | ||
if a_len < b_len { | ||
a_len.checked_mul(&2).and_then(|x| x.checked_add(&(1 - off_by_one))) | ||
} else if b_len < a_len { | ||
b_len.checked_mul(&2).and_then(|x| x.checked_add(&off_by_one)) | ||
} else { | ||
a_len.checked_mul(&2) | ||
} | ||
} | ||
|
||
// Note to developers: sequence will always look like a,b,a,b,a,... but the last element | ||
// to be yielded will be *either* an a or b. Thus we may yield one more a than b. | ||
// This is a key fact to the design of Interleave. | ||
impl<A, T: Iterator<A>, U: Iterator<A>> Iterator<A> for Interleave<T, U> { | ||
#[inline] | ||
fn next(&mut self) -> Option<A> { | ||
let use_a = self.using_first; | ||
self.using_first = !use_a; | ||
|
||
if use_a { | ||
self.a.next() | ||
} else { | ||
self.b.next() | ||
} | ||
} | ||
|
||
#[inline] | ||
fn size_hint(&self) -> (uint, Option<uint>) { | ||
let (a_lower, a_upper) = self.a.size_hint(); | ||
let (b_lower, b_upper) = self.b.size_hint(); | ||
|
||
let lower = interleave_len(a_lower, b_lower, self.using_first).unwrap_or(uint::MAX); | ||
|
||
let upper = match (a_upper, b_upper) { | ||
(Some(x), Some(y)) => interleave_len(x, y, self.using_first), | ||
// If one is missing, best we can do is assume it's huge | ||
(Some(x), None) => interleave_len(x, uint::MAX, self.using_first), | ||
(None, Some(y)) => interleave_len(uint::MAX, y, self.using_first), | ||
(None, None) => None | ||
}; | ||
|
||
(lower, upper) | ||
} | ||
} | ||
|
||
impl<A, T: ExactSize<A>, U: ExactSize<A>> DoubleEndedIterator<A> | ||
for Interleave<T, U> { | ||
#[inline] | ||
fn next_back(&mut self) -> Option<A> { | ||
let (a_sz, a_upper) = self.a.size_hint(); | ||
let (b_sz, b_upper) = self.b.size_hint(); | ||
assert!(a_upper == Some(a_sz)); | ||
assert!(b_upper == Some(b_sz)); | ||
|
||
let off_by_one = self.using_first as uint; | ||
|
||
if a_sz < b_sz { | ||
for _ in range(1 - off_by_one, b_sz - a_sz) { self.b.next_back(); } | ||
} else if a_sz > b_sz { | ||
for _ in range(off_by_one, a_sz - b_sz) { self.a.next_back(); } | ||
} | ||
let (a_sz, _) = self.a.size_hint(); | ||
let (b_sz, _) = self.b.size_hint(); | ||
assert!(a_sz == b_sz || a_sz + (1 - off_by_one) == b_sz + off_by_one); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realize this is based on the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kballard: to determine how much you've actually cut out, I guess? I mean, you can figure it out based on your range, in theory. This code seems very defensively designed. At least for my use, it just means less code spent tracking which branch I went into and edge-case interactions with almost-empty ranges. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could just make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm tempted to leave this as part of the general "assert that everything still works" pattern that seems to be around the file. I agree that it's wasteful, but it's wasteful to have the asserts at all. I'd rather be consistent and either have both or neither. Disagree? We should also update Zip to match, if we do anything. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it doesn't really matter. My preference would be to skip the asserts here, and let There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should probably be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sfackler I wish. I think it's just |
||
|
||
if (self.using_first && a_sz > b_sz) || (!self.using_first && a_sz == b_sz) { | ||
self.a.next_back() | ||
} else { | ||
self.b.next_back() | ||
} | ||
} | ||
} | ||
|
||
impl<A, T: RandomAccessIterator<A>, U: RandomAccessIterator<A>> | ||
RandomAccessIterator<A> for Interleave<T, U> { | ||
#[inline] | ||
fn indexable(&self) -> uint { | ||
interleave_len(self.a.indexable(), self.b.indexable(), self.using_first) | ||
.unwrap_or(uint::MAX) | ||
} | ||
|
||
#[inline] | ||
fn idx(&mut self, index: uint) -> Option<A> { | ||
let off_by_one = self.using_first as uint; | ||
|
||
if index < self.indexable() { | ||
if index % 2 == (1 - off_by_one) { | ||
self.a.idx((index - (1 - off_by_one)) / 2) | ||
} else { | ||
self.b.idx((index - off_by_one) / 2) | ||
} | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
|
||
/// An iterator which maps the values of `iter` with `f` | ||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] | ||
pub struct Map<'a, A, B, T> { | ||
|
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 probably isn't the PR for it, but I'm surprised
ExactSize
doesn't just vend a method.exact_size()
that includes the assert, so that way iterator adaptors don't have to keep asserting.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.
Honestly it struck me as weird to even bother with these asserts. Not trusting something that claims to implement an API to implement it strikes me as way over-defensive. But I figured if Zip does it, I probably should.
I could write up a PR to add it if you're honestly interested though, since I'm hacking away in here already.
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.
Yeah, I'm not really sure what the correct answer is. I think the asserts are here because the
ExactSize
trait demands invariants that can be broken withoutunsafe
code. So the idea is to fail early in any clients ofExactSize
if the invariant is broken, so that way the client code doesn't break its own invariants. But on the other hand, it really does seem silly to have to constantly assert that a given trait obeys its own invariants.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.
kballard it effectively does now -- the .len() method; the Zip impl just hasn't been updated to use it.
I think being overly defensive is fine; I mean that was a way to introduce the ExactSize trait without too many detractors -- I just wanted a way to sensibly define a double ended enumerate() on vec iterator; I'm not sure if every type should bother doing the same