From 15d9e197f5171708d23739c9826c62241eda1569 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Tue, 7 Nov 2023 10:40:53 +0100 Subject: [PATCH 1/3] Make `Product` lazy Similar to what is done by `core::iter::Peekable`, a nested option is now used. --- src/adaptors/mod.rs | 46 +++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 435593749..623272eae 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -308,7 +308,7 @@ where I: Iterator, { a: I, - a_cur: Option, + a_cur: Option>, b: J, b_orig: J, } @@ -316,14 +316,14 @@ where /// Create a new cartesian product iterator /// /// Iterator element type is `(I::Item, J::Item)`. -pub fn cartesian_product(mut i: I, j: J) -> Product +pub fn cartesian_product(i: I, j: J) -> Product where I: Iterator, J: Clone + Iterator, I::Item: Clone, { Product { - a_cur: i.next(), + a_cur: None, a: i, b: j.clone(), b_orig: j, @@ -339,24 +339,33 @@ where type Item = (I::Item, J::Item); fn next(&mut self) -> Option { - let elt_b = match self.b.next() { + let Self { + a, + a_cur, + b, + b_orig, + } = self; + let elt_b = match b.next() { None => { - self.b = self.b_orig.clone(); - match self.b.next() { + *b = b_orig.clone(); + match b.next() { None => return None, Some(x) => { - self.a_cur = self.a.next(); + *a_cur = Some(a.next()); x } } } Some(x) => x, }; - self.a_cur.as_ref().map(|a| (a.clone(), elt_b)) + a_cur + .get_or_insert_with(|| a.next()) + .as_ref() + .map(|a| (a.clone(), elt_b)) } fn size_hint(&self) -> (usize, Option) { - let has_cur = self.a_cur.is_some() as usize; + let has_cur = matches!(self.a_cur, Some(Some(_))) as usize; // Not ExactSizeIterator because size may be larger than usize let (b_min, b_max) = self.b.size_hint(); @@ -367,21 +376,26 @@ where ) } - fn fold(mut self, mut accum: Acc, mut f: G) -> Acc + fn fold(self, mut accum: Acc, mut f: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc, { // use a split loop to handle the loose a_cur as well as avoiding to // clone b_orig at the end. - if let Some(mut a) = self.a_cur.take() { - let mut b = self.b; + let Self { + mut a, + a_cur, + mut b, + b_orig, + } = self; + if let Some(mut elt_a) = a_cur.unwrap_or_else(|| a.next()) { loop { - accum = b.fold(accum, |acc, elt| f(acc, (a.clone(), elt))); + accum = b.fold(accum, |acc, elt| f(acc, (elt_a.clone(), elt))); // we can only continue iterating a if we had a first element; - if let Some(next_a) = self.a.next() { - b = self.b_orig.clone(); - a = next_a; + if let Some(next_elt_a) = a.next() { + b = b_orig.clone(); + elt_a = next_elt_a; } else { break; } From e03c65b8a75b67879e38cc54a06003b90efa0fd1 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Tue, 7 Nov 2023 10:59:08 +0100 Subject: [PATCH 2/3] Better `Product::size_hint` `b` size hint was computed even when not needed. --- src/adaptors/mod.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 623272eae..e3b6d0f28 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -365,15 +365,13 @@ where } fn size_hint(&self) -> (usize, Option) { - let has_cur = matches!(self.a_cur, Some(Some(_))) as usize; // Not ExactSizeIterator because size may be larger than usize - let (b_min, b_max) = self.b.size_hint(); - // Compute a * b_orig + b for both lower and upper bound - size_hint::add( - size_hint::mul(self.a.size_hint(), self.b_orig.size_hint()), - (b_min * has_cur, b_max.map(move |x| x * has_cur)), - ) + let mut sh = size_hint::mul(self.a.size_hint(), self.b_orig.size_hint()); + if matches!(self.a_cur, Some(Some(_))) { + sh = size_hint::add(sh, self.b.size_hint()); + } + sh } fn fold(self, mut accum: Acc, mut f: G) -> Acc From 3b18fd45b24be4d544a69977d9b3fb2b7bc90375 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Mon, 13 Nov 2023 19:11:59 +0100 Subject: [PATCH 3/3] Document the field `a_cur` of `Product` --- src/adaptors/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index e3b6d0f28..e925db51d 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -308,6 +308,9 @@ where I: Iterator, { a: I, + /// `a_cur` is `None` while no item have been taken out of `a` (at definition). + /// Then `a_cur` will be `Some(Some(item))` until `a` is exhausted, + /// in which case `a_cur` will be `Some(None)`. a_cur: Option>, b: J, b_orig: J,