Skip to content

Commit 23a84ab

Browse files
committed
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.
2 parents 1b55d19 + 41a4226 commit 23a84ab

File tree

5 files changed

+127
-1
lines changed

5 files changed

+127
-1
lines changed

src/liballoc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
#![feature(generic_param_attrs)]
9999
#![feature(i128_type)]
100100
#![feature(inclusive_range)]
101+
#![feature(iter_rfold)]
101102
#![feature(lang_items)]
102103
#![feature(needs_allocator)]
103104
#![feature(nonzero)]

src/liballoc/vec_deque.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1973,6 +1973,14 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
19731973
self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len());
19741974
unsafe { Some(self.ring.get_unchecked(self.head)) }
19751975
}
1976+
1977+
fn rfold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc
1978+
where F: FnMut(Acc, Self::Item) -> Acc
1979+
{
1980+
let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail);
1981+
accum = back.iter().rfold(accum, &mut f);
1982+
front.iter().rfold(accum, &mut f)
1983+
}
19761984
}
19771985

19781986
#[stable(feature = "rust1", since = "1.0.0")]
@@ -2058,6 +2066,14 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
20582066
Some(&mut *(elem as *mut _))
20592067
}
20602068
}
2069+
2070+
fn rfold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc
2071+
where F: FnMut(Acc, Self::Item) -> Acc
2072+
{
2073+
let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail);
2074+
accum = back.iter_mut().rfold(accum, &mut f);
2075+
front.iter_mut().rfold(accum, &mut f)
2076+
}
20612077
}
20622078

20632079
#[stable(feature = "rust1", since = "1.0.0")]

src/libcore/iter/iterator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,7 @@ pub trait Iterator {
13371337
(left, right)
13381338
}
13391339

1340-
/// An iterator adaptor that applies a function, producing a single, final value.
1340+
/// An iterator method that applies a function, producing a single, final value.
13411341
///
13421342
/// `fold()` takes two arguments: an initial value, and a closure with two
13431343
/// arguments: an 'accumulator', and an element. The closure returns the value that

src/libcore/iter/mod.rs

+45
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,12 @@ impl<I> Iterator for Rev<I> where I: DoubleEndedIterator {
359359
#[inline]
360360
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
361361

362+
fn fold<Acc, F>(self, init: Acc, f: F) -> Acc
363+
where F: FnMut(Acc, Self::Item) -> Acc,
364+
{
365+
self.iter.rfold(init, f)
366+
}
367+
362368
#[inline]
363369
fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
364370
where P: FnMut(&Self::Item) -> bool
@@ -379,6 +385,12 @@ impl<I> DoubleEndedIterator for Rev<I> where I: DoubleEndedIterator {
379385
#[inline]
380386
fn next_back(&mut self) -> Option<<I as Iterator>::Item> { self.iter.next() }
381387

388+
fn rfold<Acc, F>(self, init: Acc, f: F) -> Acc
389+
where F: FnMut(Acc, Self::Item) -> Acc,
390+
{
391+
self.iter.fold(init, f)
392+
}
393+
382394
fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
383395
where P: FnMut(&Self::Item) -> bool
384396
{
@@ -449,6 +461,12 @@ impl<'a, I, T: 'a> DoubleEndedIterator for Cloned<I>
449461
fn next_back(&mut self) -> Option<T> {
450462
self.it.next_back().cloned()
451463
}
464+
465+
fn rfold<Acc, F>(self, init: Acc, mut f: F) -> Acc
466+
where F: FnMut(Acc, Self::Item) -> Acc,
467+
{
468+
self.it.rfold(init, move |acc, elt| f(acc, elt.clone()))
469+
}
452470
}
453471

454472
#[stable(feature = "iter_cloned", since = "1.1.0")]
@@ -761,6 +779,26 @@ impl<A, B> DoubleEndedIterator for Chain<A, B> where
761779
ChainState::Back => self.b.next_back(),
762780
}
763781
}
782+
783+
fn rfold<Acc, F>(self, init: Acc, mut f: F) -> Acc
784+
where F: FnMut(Acc, Self::Item) -> Acc,
785+
{
786+
let mut accum = init;
787+
match self.state {
788+
ChainState::Both | ChainState::Back => {
789+
accum = self.b.rfold(accum, &mut f);
790+
}
791+
_ => { }
792+
}
793+
match self.state {
794+
ChainState::Both | ChainState::Front => {
795+
accum = self.a.rfold(accum, &mut f);
796+
}
797+
_ => { }
798+
}
799+
accum
800+
}
801+
764802
}
765803

766804
// 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
10941132
fn next_back(&mut self) -> Option<B> {
10951133
self.iter.next_back().map(&mut self.f)
10961134
}
1135+
1136+
fn rfold<Acc, G>(self, init: Acc, mut g: G) -> Acc
1137+
where G: FnMut(Acc, Self::Item) -> Acc,
1138+
{
1139+
let mut f = self.f;
1140+
self.iter.rfold(init, move |acc, elt| g(acc, f(elt)))
1141+
}
10971142
}
10981143

10991144
#[stable(feature = "rust1", since = "1.0.0")]

src/libcore/iter/traits.rs

+64
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,70 @@ pub trait DoubleEndedIterator: Iterator {
415415
#[stable(feature = "rust1", since = "1.0.0")]
416416
fn next_back(&mut self) -> Option<Self::Item>;
417417

418+
/// An iterator method that reduces the iterator's elements to a single,
419+
/// final value, starting from the back.
420+
///
421+
/// This is the reverse version of [`fold()`]: it takes elements starting from
422+
/// the back of the iterator.
423+
///
424+
/// `rfold()` takes two arguments: an initial value, and a closure with two
425+
/// arguments: an 'accumulator', and an element. The closure returns the value that
426+
/// the accumulator should have for the next iteration.
427+
///
428+
/// The initial value is the value the accumulator will have on the first
429+
/// call.
430+
///
431+
/// After applying this closure to every element of the iterator, `rfold()`
432+
/// returns the accumulator.
433+
///
434+
/// This operation is sometimes called 'reduce' or 'inject'.
435+
///
436+
/// Folding is useful whenever you have a collection of something, and want
437+
/// to produce a single value from it.
438+
///
439+
/// [`fold()`]: trait.Iterator.html#method.fold
440+
///
441+
/// # Examples
442+
///
443+
/// Basic usage:
444+
///
445+
/// ```
446+
/// #![feature(iter_rfold)]
447+
/// let a = [1, 2, 3];
448+
///
449+
/// // the sum of all of the elements of a
450+
/// let sum = a.iter()
451+
/// .rfold(0, |acc, &x| acc + x);
452+
///
453+
/// assert_eq!(sum, 6);
454+
/// ```
455+
///
456+
/// This example builds a string, starting with an initial value
457+
/// and continuing with each element from the back until the front:
458+
///
459+
/// ```
460+
/// #![feature(iter_rfold)]
461+
/// let numbers = [1, 2, 3, 4, 5];
462+
///
463+
/// let zero = "0".to_string();
464+
///
465+
/// let result = numbers.iter().rfold(zero, |acc, &x| {
466+
/// format!("({} + {})", x, acc)
467+
/// });
468+
///
469+
/// assert_eq!(result, "(1 + (2 + (3 + (4 + (5 + 0)))))");
470+
/// ```
471+
#[inline]
472+
#[unstable(feature = "iter_rfold", issue = "44705")]
473+
fn rfold<B, F>(mut self, mut accum: B, mut f: F) -> B where
474+
Self: Sized, F: FnMut(B, Self::Item) -> B,
475+
{
476+
while let Some(x) = self.next_back() {
477+
accum = f(accum, x);
478+
}
479+
accum
480+
}
481+
418482
/// Searches for an element of an iterator from the right that satisfies a predicate.
419483
///
420484
/// `rfind()` takes a closure that returns `true` or `false`. It applies

0 commit comments

Comments
 (0)