From 11bd6015267f98a8fe505129c6c63a0d99df4c2d Mon Sep 17 00:00:00 2001 From: ming li Date: Sat, 25 Jan 2020 22:46:32 -0800 Subject: [PATCH 1/2] Implement peeking_fold_while --- src/lib.rs | 35 +++++++++++++++++++ src/peeking_fold_while.rs | 70 +++++++++++++++++++++++++++++++++++++ tests/peeking_fold_while.rs | 58 ++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 src/peeking_fold_while.rs create mode 100644 tests/peeking_fold_while.rs diff --git a/src/lib.rs b/src/lib.rs index 8ced884a9..5859a6381 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,6 +116,7 @@ pub mod structs { #[cfg(feature = "use_std")] pub use multipeek_impl::MultiPeek; pub use pad_tail::PadUsing; + pub use peeking_fold_while::PeekingFoldWhile; pub use peeking_take_while::PeekingTakeWhile; #[cfg(feature = "use_std")] pub use permutations::Permutations; @@ -189,6 +190,7 @@ mod minmax; #[cfg(feature = "use_std")] mod multipeek_impl; mod pad_tail; +mod peeking_fold_while; mod peeking_take_while; #[cfg(feature = "use_std")] mod permutations; @@ -1060,6 +1062,39 @@ pub trait Itertools : Iterator { peeking_take_while::peeking_take_while(self, accept) } + /// An iterator method that applies a function to each element + /// as long as it returns successfully, producing a single value. + /// + /// Unlike `try_fold()`, `peeking_fold_while()` does not consume the element + /// that causes the function to short-circuit. + /// + /// `peeking_fold_while()` is particularly useful when the short-circuit + /// condition depends on the accumulated value. + /// + /// # Example + /// ``` + /// let a = [10, 20, 30, 100, 40, 50]; + /// + /// // Using `try_fold()` + /// let mut it = a.iter(); + /// let sum = it.try_fold(0i8, |acc, &x| acc.checked_add(x).ok_or(acc)); + /// assert_eq!(sum, Err(60)); + /// assert_eq!(it.next(), Some(&40)); + /// + /// // Using `peeking_fold_while()` + /// use itertools::Itertools; + /// let mut it = a.iter().peekable(); + /// let sum = it.peeking_fold_while(0i8, |acc, &&x| acc.checked_add(x).ok_or(acc)); + /// assert_eq!(sum, Err(60)); + /// assert_eq!(it.next(), Some(&100)); + /// ``` + fn peeking_fold_while(&mut self, init: T, f: F) -> Result + where Self: PeekingFoldWhile, + F: FnMut(T, &Self::Item) -> Result, + { + PeekingFoldWhile::peeking_fold_while(self, init, f) + } + /// Return an iterator adaptor that borrows from a `Clone`-able iterator /// to only pick off elements while the predicate `accept` returns `true`. /// diff --git a/src/peeking_fold_while.rs b/src/peeking_fold_while.rs new file mode 100644 index 000000000..e547ed5f6 --- /dev/null +++ b/src/peeking_fold_while.rs @@ -0,0 +1,70 @@ +use std::iter::Peekable; +use PutBack; +#[cfg(feature = "use_std")] +use PutBackN; + +/// A trait for folding an iterator by peeking at its elements before +/// consuming them. +pub trait PeekingFoldWhile : Iterator { + /// An iterator method that applies a function to each element + /// as long as it returns successfully, producing a single value. + /// + /// See [`.peeking_fold_while()`](../trait.Itertools.html#method.peeking_fold_while) for + /// more information. + fn peeking_fold_while(&mut self, init: T, f: F) -> Result + where F: FnMut(T, &Self::Item) -> Result; +} + +impl PeekingFoldWhile for Peekable + where I:Iterator +{ + fn peeking_fold_while(&mut self, init: T, mut f: F) -> Result + where F: FnMut(T, &I::Item) -> Result, + { + let mut acc = init; + while let Some(x) = self.peek() { + let result = f(acc, x); + if result.is_ok() { + self.next(); + } + acc = result?; + } + Ok(acc) + } +} + +impl PeekingFoldWhile for PutBack + where I: Iterator +{ + fn peeking_fold_while(&mut self, init: T, mut f: F) -> Result + where F: FnMut(T, &I::Item) -> Result, + { + let mut acc = init; + while let Some(x) = self.next() { + let result = f(acc, &x); + if result.is_err() { + self.put_back(x); + } + acc = result?; + } + Ok(acc) + } +} + +impl PeekingFoldWhile for PutBackN + where I: Iterator +{ + fn peeking_fold_while(&mut self, init: T, mut f: F) -> Result + where F: FnMut(T, &I::Item) -> Result, + { + let mut acc = init; + while let Some(x) = self.next() { + let result = f(acc, &x); + if result.is_err() { + self.put_back(x); + } + acc = result?; + } + Ok(acc) + } +} diff --git a/tests/peeking_fold_while.rs b/tests/peeking_fold_while.rs new file mode 100644 index 000000000..fc7621f76 --- /dev/null +++ b/tests/peeking_fold_while.rs @@ -0,0 +1,58 @@ +extern crate itertools; + +use itertools::Itertools; +use itertools::{put_back, put_back_n}; + +#[test] +fn peeking_fold_while_peekable_consumes_all() { + let a = [10, 20, 30]; + let mut it = a.iter().peekable(); + let sum = it.peeking_fold_while(0i8, |acc, &&x| acc.checked_add(x).ok_or(acc)); + assert_eq!(sum, Ok(60)); + assert_eq!(it.next(), None); +} + +#[test] +fn peeking_fold_while_peekable_consumes_some() { + let a = [10, 20, 30, 100, 40, 50]; + let mut it = a.iter().peekable(); + let sum = it.peeking_fold_while(0i8, |acc, &&x| acc.checked_add(x).ok_or(acc)); + assert_eq!(sum, Err(60)); + assert_eq!(it.next(), Some(&100)); +} + +#[test] +fn peeking_fold_while_put_back_consumes_all() { + let a = [10, 20, 30]; + let mut it = put_back(a.iter()); + let sum = it.peeking_fold_while(0i8, |acc, &&x| acc.checked_add(x).ok_or(acc)); + assert_eq!(sum, Ok(60)); + assert_eq!(it.next(), None); +} + +#[test] +fn peeking_fold_while_put_back_consumes_some() { + let a = [10, 20, 30, 100, 40, 50]; + let mut it = put_back(a.iter()); + let sum = it.peeking_fold_while(0i8, |acc, &&x| acc.checked_add(x).ok_or(acc)); + assert_eq!(sum, Err(60)); + assert_eq!(it.next(), Some(&100)); +} + +#[test] +fn peeking_fold_while_put_back_n_consumes_all() { + let a = [10, 20, 30]; + let mut it = put_back_n(a.iter()); + let sum = it.peeking_fold_while(0i8, |acc, &&x| acc.checked_add(x).ok_or(acc)); + assert_eq!(sum, Ok(60)); + assert_eq!(it.next(), None); +} + +#[test] +fn peeking_fold_while_put_back_n_consumes_some() { + let a = [10, 20, 30, 100, 40, 50]; + let mut it = put_back(a.iter()); + let sum = it.peeking_fold_while(0i8, |acc, &&x| acc.checked_add(x).ok_or(acc)); + assert_eq!(sum, Err(60)); + assert_eq!(it.next(), Some(&100)); +} From ad3c8de8bf9660602cfc55706b7ac051a213b14b Mon Sep 17 00:00:00 2001 From: ming li Date: Sun, 26 Jan 2020 11:57:15 -0800 Subject: [PATCH 2/2] Cfg use_std for PutBackN --- src/peeking_fold_while.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/peeking_fold_while.rs b/src/peeking_fold_while.rs index e547ed5f6..ddf9e54aa 100644 --- a/src/peeking_fold_while.rs +++ b/src/peeking_fold_while.rs @@ -51,6 +51,7 @@ impl PeekingFoldWhile for PutBack } } +#[cfg(feature = "use_std")] impl PeekingFoldWhile for PutBackN where I: Iterator {