extend the iterator tutorial

documents conversion, size hints and double-ended iterators and adds
more of the traits to the prelude
This commit is contained in:
Daniel Micay 2013-07-11 21:10:59 -04:00
parent 07183ea6e7
commit 2b96408600
5 changed files with 115 additions and 15 deletions

View File

@ -205,3 +205,104 @@ println(fmt!("last: %?", it.next()));
// the iterator is now fully consumed
assert!(it.next().is_none());
~~~
## Conversion
Iterators offer generic conversion to containers with the `collect` adaptor:
~~~
let xs = [0, 1, 1, 2, 3, 5, 8];
let ys = xs.rev_iter().skip(1).transform(|&x| x * 2).collect::<~[int]>();
assert_eq!(ys, ~[10, 6, 4, 2, 2, 0]);
~~~
The method requires a type hint for the container type, if the surrounding code
does not provide sufficient information.
Containers can provide conversion from iterators through `collect` by
implementing the `FromIterator` trait. For example, the implementation for
vectors is as follows:
~~~
impl<A, T: Iterator<A>> FromIterator<A, T> for ~[A] {
pub fn from_iterator(iterator: &mut T) -> ~[A] {
let (lower, _) = iterator.size_hint();
let mut xs = with_capacity(lower);
for iterator.advance |x| {
xs.push(x);
}
xs
}
}
~~~
### Size hints
The `Iterator` trait provides a `size_hint` default method, returning a lower
bound and optionally on upper bound on the length of the iterator:
~~~
fn size_hint(&self) -> (uint, Option<uint>) { (0, None) }
~~~
The vector implementation of `FromIterator` from above uses the lower bound
to pre-allocate enough space to hold the minimum number of elements the
iterator will yield.
The default implementation is always correct, but it should be overridden if
the iterator can provide better information.
The `ZeroStream` from earlier can provide an exact lower and upper bound:
~~~
/// A stream of N zeroes
struct ZeroStream {
priv remaining: uint
}
impl ZeroStream {
fn new(n: uint) -> ZeroStream {
ZeroStream { remaining: n }
}
fn size_hint(&self) -> (uint, Option<uint>) {
(self.remaining, Some(self.remaining))
}
}
impl Iterator<int> for ZeroStream {
fn next(&mut self) -> Option<int> {
if self.remaining == 0 {
None
} else {
self.remaining -= 1;
Some(0)
}
}
}
~~~
## Double-ended iterators
The `DoubleEndedIterator` trait represents an iterator able to yield elements
from either end of a range. It inherits from the `Iterator` trait and extends
it with the `next_back` function.
A `DoubleEndedIterator` can be flipped with the `invert` adaptor, returning
another `DoubleEndedIterator` with `next` and `next_back` exchanged.
~~~
let xs = [1, 2, 3, 4, 5, 6];
let mut it = xs.iter();
println(fmt!("%?", it.next())); // prints `Some(&1)`
println(fmt!("%?", it.next())); // prints `Some(&2)`
println(fmt!("%?", it.next_back())); // prints `Some(&6)`
// prints `5`, `4` and `3`
for it.invert().advance |&x| {
println(fmt!("%?", x))
}
~~~
The `rev_iter` and `mut_rev_iter` methods on vectors just return an inverted
version of the standard immutable and mutable vector iterators.

View File

@ -104,7 +104,7 @@ impl SmallBitv {
}
#[inline]
pub fn invert(&mut self) { self.bits = !self.bits; }
pub fn negate(&mut self) { self.bits = !self.bits; }
}
struct BigBitv {
@ -160,7 +160,7 @@ impl BigBitv {
}
#[inline]
pub fn invert(&mut self) { for self.each_storage |w| { *w = !*w } }
pub fn negate(&mut self) { for self.each_storage |w| { *w = !*w } }
#[inline]
pub fn union(&mut self, b: &BigBitv, nbits: uint) -> bool {
@ -366,9 +366,9 @@ impl Bitv {
/// Invert all bits
#[inline]
pub fn invert(&mut self) {
pub fn negate(&mut self) {
match self.rep {
Small(ref mut b) => b.invert(),
Small(ref mut b) => b.negate(),
Big(ref mut s) => for s.each_storage() |w| { *w = !*w } }
}

View File

@ -47,8 +47,9 @@ pub use cmp::{Eq, ApproxEq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Great
pub use char::Char;
pub use container::{Container, Mutable, Map, Set};
pub use hash::Hash;
pub use iter::{Times};
pub use iterator::{Iterator, IteratorUtil, OrdIterator};
pub use iter::Times;
pub use iterator::{Iterator, IteratorUtil, DoubleEndedIterator, DoubleEndedIteratorUtil};
pub use iterator::OrdIterator;
pub use num::{Num, NumCast};
pub use num::{Orderable, Signed, Unsigned, Round};
pub use num::{Algebraic, Trigonometric, Exponential, Hyperbolic};

View File

@ -760,6 +760,7 @@ impl<'self,T> ImmutableVector<'self, T> for &'self [T] {
lifetime: cast::transmute(p)}
}
}
#[inline]
fn rev_iter(self) -> VecRevIterator<'self, T> {
self.iter().invert()
@ -2211,7 +2212,6 @@ impl<A, T: Iterator<A>> FromIterator<A, T> for ~[A] {
}
}
#[cfg(test)]
mod tests {
use option::{None, Option, Some};

View File

@ -11,19 +11,17 @@
use std::vec;
trait sum {
fn sum(self) -> int;
fn sum_(self) -> int;
}
// Note: impl on a slice
impl<'self> sum for &'self [int] {
fn sum(self) -> int {
let mut sum = 0;
for self.iter().advance |e| { sum += *e; }
return sum;
fn sum_(self) -> int {
self.iter().fold(0, |a, &b| a + b)
}
}
fn call_sum(x: &[int]) -> int { x.sum() }
fn call_sum(x: &[int]) -> int { x.sum_() }
pub fn main() {
let x = ~[1, 2, 3];
@ -32,12 +30,12 @@ pub fn main() {
assert_eq!(y, 6);
let mut x = ~[1, 2, 3];
let y = x.sum();
let y = x.sum_();
debug!("y==%d", y);
assert_eq!(y, 6);
let x = ~[1, 2, 3];
let y = x.sum();
let y = x.sum_();
debug!("y==%d", y);
assert_eq!(y, 6);
}