From 142659f3c4e472b8800e16cb55cebbfbf3f78c6d Mon Sep 17 00:00:00 2001 From: Emerentius Date: Wed, 15 Aug 2018 19:25:51 +0200 Subject: [PATCH] Add Itertools::{sum1, product1} Like fold1, these return Option Some(result) if the iter is nonempty, None otherwise. Allows differentiation between empty iterators and iterators that reduce to neutral elements --- src/lib.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++- tests/test_core.rs | 18 ++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5f3eb72a1..d7c7960bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ pub use either::Either; #[cfg(feature = "use_std")] use std::collections::HashMap; -use std::iter::{IntoIterator}; +use std::iter::{IntoIterator, once}; use std::cmp::Ordering; use std::fmt; #[cfg(feature = "use_std")] @@ -1928,6 +1928,64 @@ pub trait Itertools : Iterator { FoldWhile::Continue(acc) } + /// Iterate over the entire iterator and add all the elements. + /// + /// An empty iterator returns `None`, otherwise `Some(sum)`. + /// + /// # Panics + /// + /// When calling `sum1()` and a primitive integer type is being returned, this + /// method will panic if the computation overflows and debug assertions are + /// enabled. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// + /// let empty_sum = (1..1).sum1::(); + /// assert_eq!(empty_sum, None); + /// + /// let nonempty_sum = (1..11).sum1::(); + /// assert_eq!(nonempty_sum, Some(55)); + /// ``` + fn sum1(mut self) -> Option + where Self: Sized, + S: std::iter::Sum, + { + self.next() + .map(|first| once(first).chain(self).sum()) + } + + /// Iterate over the entire iterator and multiply all the elements. + /// + /// An empty iterator returns `None`, otherwise `Some(product)`. + /// + /// # Panics + /// + /// When calling `product1()` and a primitive integer type is being returned, + /// method will panic if the computation overflows and debug assertions are + /// enabled. + /// + /// # Examples + /// ``` + /// use itertools::Itertools; + /// + /// let empty_product = (1..1).product1::(); + /// assert_eq!(empty_product, None); + /// + /// let nonempty_product = (1..11).product1::(); + /// assert_eq!(nonempty_product, Some(3628800)); + /// ``` + fn product1

(mut self) -> Option

+ where Self: Sized, + P: std::iter::Product, + { + self.next() + .map(|first| once(first).chain(self).product()) + } + + /// Sort all iterator elements into a new iterator in ascending order. /// /// **Note:** This consumes the entire iterator, uses the diff --git a/tests/test_core.rs b/tests/test_core.rs index 7007217da..b1e03cf67 100644 --- a/tests/test_core.rs +++ b/tests/test_core.rs @@ -252,3 +252,21 @@ fn exactly_one() { assert!((0..10).filter(|&x| x > 1 && x < 5).exactly_one().unwrap_err().eq(2..5)); assert!((0..10).filter(|&_| false).exactly_one().unwrap_err().eq(0..0)); } + +#[test] +fn sum1() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..0].iter().cloned().sum1::(), None); + assert_eq!(v[1..2].iter().cloned().sum1::(), Some(1)); + assert_eq!(v[1..3].iter().cloned().sum1::(), Some(3)); + assert_eq!(v.iter().cloned().sum1::(), Some(55)); +} + +#[test] +fn product1() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..0].iter().cloned().product1::(), None); + assert_eq!(v[..1].iter().cloned().product1::(), Some(0)); + assert_eq!(v[1..3].iter().cloned().product1::(), Some(2)); + assert_eq!(v[1..5].iter().cloned().product1::(), Some(24)); +}