From ffd171e47f6cdd5b05e377cbcf088070720f31bc Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 18 Sep 2017 21:18:15 +0200 Subject: [PATCH 1/7] core: Small fix in fold docs Adaptors are things that take iterators and adapt them into other iterators. With this definition, fold is just a usual method, because it doesn't normally make an iterator. --- src/libcore/iter/iterator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 7c009114afe..aacc29406b6 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -1341,7 +1341,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 From 91318c8cab25504472deb5cd89e6a11fed60fe13 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 18 Sep 2017 21:19:58 +0200 Subject: [PATCH 2/7] core: Add DoubleEndedIterator::rfold 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 adaptor connect Rev::rfold -> I::fold, Rev::fold -> I::rfold. --- src/libcore/iter/traits.rs | 62 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 2af129a67bd..85df8a669d1 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -398,6 +398,68 @@ pub trait DoubleEndedIterator: Iterator { #[stable(feature = "rust1", since = "1.0.0")] fn next_back(&mut self) -> Option; + /// 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: + /// + /// ``` + /// 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: + /// + /// ``` + /// 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 = "0")] + fn rfold(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 From 31cf26a953fe69c23de49f98676a746fea5dcefb Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 18 Sep 2017 21:24:33 +0200 Subject: [PATCH 3/7] core: Implement fold / rfold for Rev With both in place, we can cross them over in rev, and we give rfold behaviour to .rev().fold() and so on. --- src/libcore/iter/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index ebedfe1d743..65cd28b499e 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -359,6 +359,12 @@ impl Iterator for Rev where I: DoubleEndedIterator { #[inline] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + fn fold(self, init: Acc, f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.rfold(init, f) + } + #[inline] fn find

(&mut self, predicate: P) -> Option where P: FnMut(&Self::Item) -> bool @@ -379,6 +385,12 @@ impl DoubleEndedIterator for Rev where I: DoubleEndedIterator { #[inline] fn next_back(&mut self) -> Option<::Item> { self.iter.next() } + fn rfold(self, init: Acc, f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, f) + } + fn rfind

(&mut self, predicate: P) -> Option where P: FnMut(&Self::Item) -> bool { From a59a25d8e64deead0721b6e537a29f600227718f Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 18 Sep 2017 21:27:19 +0200 Subject: [PATCH 4/7] core: Implement rfold for Map, Cloned, Chain --- src/libcore/iter/mod.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 65cd28b499e..cee345d52ca 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -461,6 +461,12 @@ impl<'a, I, T: 'a> DoubleEndedIterator for Cloned fn next_back(&mut self) -> Option { self.it.next_back().cloned() } + + fn rfold(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")] @@ -773,6 +779,26 @@ impl DoubleEndedIterator for Chain where ChainState::Back => self.b.next_back(), } } + + fn rfold(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. @@ -1106,6 +1132,13 @@ impl DoubleEndedIterator for Map where fn next_back(&mut self) -> Option { self.iter.next_back().map(&mut self.f) } + + fn rfold(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")] From 84c90f30226f4cc7998ed5d74aea872252c89be9 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 18 Sep 2017 21:52:13 +0200 Subject: [PATCH 5/7] alloc: Implement rfold for VecDeque iterators --- src/liballoc/lib.rs | 1 + src/liballoc/vec_deque.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index dc64a787ae9..ac2f72d5fa1 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(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(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")] From 7e81cee934e37409591ce687d290ba6ac938c894 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 18 Sep 2017 23:09:00 +0200 Subject: [PATCH 6/7] core: Add feature gate to rfold example code --- src/libcore/iter/traits.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 85df8a669d1..bf74ed826bf 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -426,6 +426,7 @@ pub trait DoubleEndedIterator: Iterator { /// Basic usage: /// /// ``` + /// #![feature(iter_rfold)] /// let a = [1, 2, 3]; /// /// // the sum of all of the elements of a @@ -439,6 +440,7 @@ pub trait DoubleEndedIterator: Iterator { /// 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(); From 41a42263dffdfed9076029a3db973e1efad5f792 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Tue, 19 Sep 2017 21:24:04 +0200 Subject: [PATCH 7/7] core: Assign tracking issue for iter_rfold --- src/libcore/iter/traits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index bf74ed826bf..4386b0a6456 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -452,7 +452,7 @@ pub trait DoubleEndedIterator: Iterator { /// assert_eq!(result, "(1 + (2 + (3 + (4 + (5 + 0)))))"); /// ``` #[inline] - #[unstable(feature = "iter_rfold", issue = "0")] + #[unstable(feature = "iter_rfold", issue = "44705")] fn rfold(mut self, mut accum: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, {