From: Mazdak Farrokhzad Date: Tue, 20 Feb 2018 07:26:30 +0000 (+0100) Subject: add Iterator::flatten and redefine flat_map(f) in terms of map(f).flatten() X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=6af23f977c44fc67d8611b2581c334e795999bcd;p=rust.git add Iterator::flatten and redefine flat_map(f) in terms of map(f).flatten() --- diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 33adb3f49dd..8ed3450dc3a 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -12,7 +12,7 @@ use ops::Try; use super::{AlwaysOk, LoopState}; -use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, FlatMap, Fuse}; +use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, Flatten, FlatMap, Fuse}; use super::{Inspect, Map, Peekable, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, Rev}; use super::{Zip, Sum, Product}; use super::{ChainState, FromIterator, ZipImpl}; @@ -997,11 +997,15 @@ fn scan(self, initial_state: St, f: F) -> Scan /// an extra layer of indirection. `flat_map()` will remove this extra layer /// on its own. /// + /// You can think of [`flat_map(f)`][flat_map] as the equivalent of + /// [`map`]ping, and then [`flatten`]ing as in `map(f).flatten()`. + /// /// Another way of thinking about `flat_map()`: [`map`]'s closure returns /// one item for each element, and `flat_map()`'s closure returns an /// iterator for each element. /// /// [`map`]: #method.map + /// [`flatten`]: #method.flatten /// /// # Examples /// @@ -1021,7 +1025,46 @@ fn scan(self, initial_state: St, f: F) -> Scan fn flat_map(self, f: F) -> FlatMap where Self: Sized, U: IntoIterator, F: FnMut(Self::Item) -> U, { - FlatMap{iter: self, f: f, frontiter: None, backiter: None } + self.map(f).flatten() + } + + /// Creates an iterator that flattens nested structure. + /// + /// This is useful when you have an iterator of iterators or an iterator of + /// things that can be turned into iterators and you want to remove one + /// level of indirection. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iterator_flatten)] + /// + /// let data = vec![vec![1, 2, 3, 4], vec![5, 6]]; + /// let flattened = data.into_iter().flatten().collect::>(); + /// assert_eq!(flattened, &[1, 2, 3, 4, 5, 6]); + /// ``` + /// + /// Mapping and then flattening: + /// + /// ``` + /// #![feature(iterator_flatten)] + /// + /// let words = ["alpha", "beta", "gamma"]; + /// + /// // chars() returns an iterator + /// let merged: String = words.iter() + /// .map(|s| s.chars()) + /// .flatten() + /// .collect(); + /// assert_eq!(merged, "alphabetagamma"); + /// ``` + #[inline] + #[unstable(feature = "iterator_flatten", issue = "0")] + fn flatten(self) -> Flatten::IntoIter> + where Self: Sized, Self::Item: IntoIterator { + Flatten { iter: self, frontiter: None, backiter: None } } /// Creates an iterator which ends after the first [`None`]. diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 1a2da83429a..bd801d2ae69 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -2403,37 +2403,35 @@ fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R where /// An iterator that maps each element to an iterator, and yields the elements /// of the produced iterators. /// -/// This `struct` is created by the [`flat_map`] method on [`Iterator`]. See its +/// This `type` is created by the [`flat_map`] method on [`Iterator`]. See its /// documentation for more. /// /// [`flat_map`]: trait.Iterator.html#method.flat_map /// [`Iterator`]: trait.Iterator.html #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct FlatMap { - iter: I, - f: F, - frontiter: Option, - backiter: Option, -} +type FlatMap = Flatten, ::IntoIter>; -#[stable(feature = "core_impl_debug", since = "1.9.0")] -impl fmt::Debug for FlatMap - where U::IntoIter: fmt::Debug -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("FlatMap") - .field("iter", &self.iter) - .field("frontiter", &self.frontiter) - .field("backiter", &self.backiter) - .finish() - } +/// An iterator that flattens one level of nesting in an iterator of things +/// that can be turned into iterators. +/// +/// This `struct` is created by the [`flatten`] method on [`Iterator`]. See its +/// documentation for more. +/// +/// [`flatten`]: trait.Iterator.html#method.flatten +/// [`Iterator`]: trait.Iterator.html +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[unstable(feature = "iterator_flatten", issue = "0")] +#[derive(Clone, Debug)] +pub struct Flatten { + iter: I, + frontiter: Option, + backiter: Option, } -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for FlatMap - where F: FnMut(I::Item) -> U, +#[unstable(feature = "iterator_flatten", issue = "0")] +impl Iterator for Flatten + where I::Item: IntoIterator { type Item = U::Item; @@ -2441,13 +2439,11 @@ impl Iterator for FlatMap fn next(&mut self) -> Option { loop { if let Some(ref mut inner) = self.frontiter { - if let Some(x) = inner.by_ref().next() { - return Some(x) - } + if let elt@Some(_) = inner.next() { return elt } } - match self.iter.next().map(&mut self.f) { + match self.iter.next() { None => return self.backiter.as_mut().and_then(|it| it.next()), - next => self.frontiter = next.map(IntoIterator::into_iter), + Some(inner) => self.frontiter = Some(inner.into_iter()), } } } @@ -2473,10 +2469,9 @@ fn try_fold(&mut self, mut init: Acc, mut fold: Fold) -> R where self.frontiter = None; { - let f = &mut self.f; let frontiter = &mut self.frontiter; init = self.iter.try_fold(init, |acc, x| { - let mut mid = f(x).into_iter(); + let mut mid = x.into_iter(); let r = mid.try_fold(acc, &mut fold); *frontiter = Some(mid); r @@ -2497,27 +2492,24 @@ fn fold(self, init: Acc, mut fold: Fold) -> Acc where Fold: FnMut(Acc, Self::Item) -> Acc, { self.frontiter.into_iter() - .chain(self.iter.map(self.f).map(U::into_iter)) + .chain(self.iter.map(IntoIterator::into_iter)) .chain(self.backiter) .fold(init, |acc, iter| iter.fold(acc, &mut fold)) } } -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for FlatMap where - F: FnMut(I::Item) -> U, - U: IntoIterator, - U::IntoIter: DoubleEndedIterator +#[unstable(feature = "iterator_flatten", issue = "0")] +impl DoubleEndedIterator for Flatten + where I: DoubleEndedIterator, U: DoubleEndedIterator, + I::Item: IntoIterator { #[inline] fn next_back(&mut self) -> Option { loop { if let Some(ref mut inner) = self.backiter { - if let Some(y) = inner.next_back() { - return Some(y) - } + if let elt@Some(_) = inner.next_back() { return elt } } - match self.iter.next_back().map(&mut self.f) { + match self.iter.next_back() { None => return self.frontiter.as_mut().and_then(|it| it.next_back()), next => self.backiter = next.map(IntoIterator::into_iter), } @@ -2534,10 +2526,9 @@ fn try_rfold(&mut self, mut init: Acc, mut fold: Fold) -> R where self.backiter = None; { - let f = &mut self.f; let backiter = &mut self.backiter; init = self.iter.try_rfold(init, |acc, x| { - let mut mid = f(x).into_iter(); + let mut mid = x.into_iter(); let r = mid.try_rfold(acc, &mut fold); *backiter = Some(mid); r @@ -2558,15 +2549,15 @@ fn rfold(self, init: Acc, mut fold: Fold) -> Acc where Fold: FnMut(Acc, Self::Item) -> Acc, { self.frontiter.into_iter() - .chain(self.iter.map(self.f).map(U::into_iter)) + .chain(self.iter.map(IntoIterator::into_iter)) .chain(self.backiter) .rfold(init, |acc, iter| iter.rfold(acc, &mut fold)) } } -#[unstable(feature = "fused", issue = "35602")] -impl FusedIterator for FlatMap - where I: FusedIterator, U: IntoIterator, F: FnMut(I::Item) -> U {} +#[unstable(feature = "fused", issue = "0")] +impl FusedIterator for Flatten + where I::Item: IntoIterator {} /// An iterator that yields `None` forever after the underlying iterator /// yields `None` once. diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index d2162d307e0..3dd30ee1c69 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -93,6 +93,7 @@ #![feature(doc_spotlight)] #![feature(rustc_const_unstable)] #![feature(iterator_repeat_with)] +#![feature(iterator_flatten)] #[prelude_import] #[allow(unused)] diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index f91c919d744..f28f5ef181c 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -836,6 +836,8 @@ fn add(old: &mut isize, new: &usize) -> Option { assert_eq!(i, ys.len()); } +// Note: We test flatten() by testing flat_map(). + #[test] fn test_iterator_flat_map() { let xs = [0, 3, 6];