diff options
| author | bors <bors@rust-lang.org> | 2017-09-21 23:44:11 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-09-21 23:44:11 +0000 |
| commit | 17600c1ea77ad8709ea072ad1b258bf9398b9d38 (patch) | |
| tree | c422cfcca0b9f5e27f0475979dc3a408822a82b0 | |
| parent | 17f56c549c35bb2cb316e5abff116e65277c7bb1 (diff) | |
| parent | 41a42263dffdfed9076029a3db973e1efad5f792 (diff) | |
Auto merge of #44682 - bluss:iter-rfold, r=dtolnay
Add iterator method .rfold(init, function); the reverse of fold rfold is the reverse version of fold. Fold allows iterators to implement a different (non-resumable) internal iteration when it is more efficient than the external iteration implemented through the next method. (Common examples are VecDeque and .chain()). Introduce rfold() so that the same customization is available for reverse iteration. This is achieved by both adding the method, and by having the Rev\<I> adaptor connect Rev::rfold → I::fold and Rev::fold → I::rfold. On the surface, rfold(..) is just .rev().fold(..), but the special case implementations allow a data structure specific fold to be used through for example .iter().rev(); we thus have gains even for users never calling exactly rfold themselves.
| -rw-r--r-- | src/liballoc/lib.rs | 1 | ||||
| -rw-r--r-- | src/liballoc/vec_deque.rs | 16 | ||||
| -rw-r--r-- | src/libcore/iter/iterator.rs | 2 | ||||
| -rw-r--r-- | src/libcore/iter/mod.rs | 45 | ||||
| -rw-r--r-- | src/libcore/iter/traits.rs | 64 |
5 files changed, 127 insertions, 1 deletions
diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 2845d349ae1..d51aaa23c6a 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -98,6 +98,7 @@ #![feature(generic_param_attrs)] #![feature(i128_type)] #![feature(inclusive_range)] +#![feature(iter_rfold)] #![feature(lang_items)] #![feature(needs_allocator)] #![feature(nonzero)] diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index 00def2a1eac..6836fbb7c4d 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -1973,6 +1973,14 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len()); unsafe { Some(self.ring.get_unchecked(self.head)) } } + + fn rfold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc + { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + accum = back.iter().rfold(accum, &mut f); + front.iter().rfold(accum, &mut f) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2058,6 +2066,14 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { Some(&mut *(elem as *mut _)) } } + + fn rfold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc + { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + accum = back.iter_mut().rfold(accum, &mut f); + front.iter_mut().rfold(accum, &mut f) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index edafd0ce2c2..36bf9633b4a 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -1337,7 +1337,7 @@ pub trait Iterator { (left, right) } - /// An iterator adaptor that applies a function, producing a single, final value. + /// An iterator method that applies a function, producing a single, final value. /// /// `fold()` takes two arguments: an initial value, and a closure with two /// arguments: an 'accumulator', and an element. The closure returns the value that diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index a596ffd6ae8..7907f2fd661 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -359,6 +359,12 @@ impl<I> Iterator for Rev<I> where I: DoubleEndedIterator { #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } + fn fold<Acc, F>(self, init: Acc, f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.rfold(init, f) + } + #[inline] fn find<P>(&mut self, predicate: P) -> Option<Self::Item> where P: FnMut(&Self::Item) -> bool @@ -379,6 +385,12 @@ impl<I> DoubleEndedIterator for Rev<I> where I: DoubleEndedIterator { #[inline] fn next_back(&mut self) -> Option<<I as Iterator>::Item> { self.iter.next() } + fn rfold<Acc, F>(self, init: Acc, f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, f) + } + fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item> where P: FnMut(&Self::Item) -> bool { @@ -449,6 +461,12 @@ impl<'a, I, T: 'a> DoubleEndedIterator for Cloned<I> fn next_back(&mut self) -> Option<T> { self.it.next_back().cloned() } + + fn rfold<Acc, F>(self, init: Acc, mut f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc, + { + self.it.rfold(init, move |acc, elt| f(acc, elt.clone())) + } } #[stable(feature = "iter_cloned", since = "1.1.0")] @@ -761,6 +779,26 @@ impl<A, B> DoubleEndedIterator for Chain<A, B> where ChainState::Back => self.b.next_back(), } } + + fn rfold<Acc, F>(self, init: Acc, mut f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc, + { + let mut accum = init; + match self.state { + ChainState::Both | ChainState::Back => { + accum = self.b.rfold(accum, &mut f); + } + _ => { } + } + match self.state { + ChainState::Both | ChainState::Front => { + accum = self.a.rfold(accum, &mut f); + } + _ => { } + } + accum + } + } // Note: *both* must be fused to handle double-ended iterators. @@ -1094,6 +1132,13 @@ impl<B, I: DoubleEndedIterator, F> DoubleEndedIterator for Map<I, F> where fn next_back(&mut self) -> Option<B> { self.iter.next_back().map(&mut self.f) } + + fn rfold<Acc, G>(self, init: Acc, mut g: G) -> Acc + where G: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.rfold(init, move |acc, elt| g(acc, f(elt))) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 7ef50396c82..28236d193c3 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -415,6 +415,70 @@ pub trait DoubleEndedIterator: Iterator { #[stable(feature = "rust1", since = "1.0.0")] fn next_back(&mut self) -> Option<Self::Item>; + /// An iterator method that reduces the iterator's elements to a single, + /// final value, starting from the back. + /// + /// This is the reverse version of [`fold()`]: it takes elements starting from + /// the back of the iterator. + /// + /// `rfold()` takes two arguments: an initial value, and a closure with two + /// arguments: an 'accumulator', and an element. The closure returns the value that + /// the accumulator should have for the next iteration. + /// + /// The initial value is the value the accumulator will have on the first + /// call. + /// + /// After applying this closure to every element of the iterator, `rfold()` + /// returns the accumulator. + /// + /// This operation is sometimes called 'reduce' or 'inject'. + /// + /// Folding is useful whenever you have a collection of something, and want + /// to produce a single value from it. + /// + /// [`fold()`]: trait.Iterator.html#method.fold + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_rfold)] + /// let a = [1, 2, 3]; + /// + /// // the sum of all of the elements of a + /// let sum = a.iter() + /// .rfold(0, |acc, &x| acc + x); + /// + /// assert_eq!(sum, 6); + /// ``` + /// + /// This example builds a string, starting with an initial value + /// and continuing with each element from the back until the front: + /// + /// ``` + /// #![feature(iter_rfold)] + /// let numbers = [1, 2, 3, 4, 5]; + /// + /// let zero = "0".to_string(); + /// + /// let result = numbers.iter().rfold(zero, |acc, &x| { + /// format!("({} + {})", x, acc) + /// }); + /// + /// assert_eq!(result, "(1 + (2 + (3 + (4 + (5 + 0)))))"); + /// ``` + #[inline] + #[unstable(feature = "iter_rfold", issue = "44705")] + fn rfold<B, F>(mut self, mut accum: B, mut f: F) -> B where + Self: Sized, F: FnMut(B, Self::Item) -> B, + { + while let Some(x) = self.next_back() { + accum = f(accum, x); + } + accum + } + /// Searches for an element of an iterator from the right that satisfies a predicate. /// /// `rfind()` takes a closure that returns `true` or `false`. It applies |
