From 4d43160159c3f5d8ff0a4aa73aaab6ae1295e60c Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Mon, 2 Oct 2023 11:28:34 +0200 Subject: [PATCH 1/4] impl `Eq` for `Position` I do not see any reason not to and I need this for the next commit. --- src/with_position.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/with_position.rs b/src/with_position.rs index 9cdc8319e..58802c5ff 100644 --- a/src/with_position.rs +++ b/src/with_position.rs @@ -37,7 +37,7 @@ where /// Indicates the position of this element in the iterator results. /// /// See [`.with_position()`](crate::Itertools::with_position) for more information. -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Position { /// This is the first element. First, From 4f16d72cf27155f3866e03539343848a33dada90 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Mon, 2 Oct 2023 11:30:35 +0200 Subject: [PATCH 2/4] Test `WithPosition` specializations --- tests/specializations.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/specializations.rs b/tests/specializations.rs index f4463a1b2..4fa6c33df 100644 --- a/tests/specializations.rs +++ b/tests/specializations.rs @@ -74,6 +74,10 @@ where } quickcheck! { + fn with_position(v: Vec) -> () { + test_specializations(&v.iter().with_position()); + } + fn tuple_combinations(v: Vec) -> () { let mut v = v; v.truncate(10); From 6317878b0f7e1aecf932abbd3b89fa0709ec8f40 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Mon, 2 Oct 2023 12:08:21 +0200 Subject: [PATCH 3/4] Benchmark `WithPosition::fold` --- benches/bench1.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/benches/bench1.rs b/benches/bench1.rs index 8a384ee69..fbcaa05a7 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -1,6 +1,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use itertools::free::cloned; use itertools::Itertools; +use itertools::Position; use itertools::{iproduct, EitherOrBoth}; use std::cmp; @@ -819,6 +820,22 @@ fn permutations_slice(c: &mut Criterion) { }); } +fn with_position_fold(c: &mut Criterion) { + let v = black_box((0..10240).collect_vec()); + c.bench_function("with_position fold", move |b| { + b.iter(|| { + v.iter() + .with_position() + .fold(0, |acc, (pos, &item)| match pos { + Position::Middle => acc + item, + Position::First => acc - 2 * item, + Position::Last => acc + 2 * item, + Position::Only => acc + 5 * item, + }) + }) + }); +} + criterion_group!( benches, slice_iter, @@ -866,5 +883,6 @@ criterion_group!( permutations_iter, permutations_range, permutations_slice, + with_position_fold, ); criterion_main!(benches); From 4742e9f475deaf61b87307e56af9d6ee5f2a2131 Mon Sep 17 00:00:00 2001 From: Philippe-Cholet Date: Mon, 2 Oct 2023 11:10:24 +0200 Subject: [PATCH 4/4] `WithPosition::fold` --- src/with_position.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/with_position.rs b/src/with_position.rs index 58802c5ff..89cddeb8a 100644 --- a/src/with_position.rs +++ b/src/with_position.rs @@ -81,6 +81,33 @@ impl Iterator for WithPosition { fn size_hint(&self) -> (usize, Option) { self.peekable.size_hint() } + + fn fold(mut self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + if let Some(mut head) = self.peekable.next() { + if !self.handled_first { + // The current head is `First` or `Only`, + // it depends if there is another item or not. + match self.peekable.next() { + Some(second) => { + let first = std::mem::replace(&mut head, second); + init = f(init, (Position::First, first)); + } + None => return f(init, (Position::Only, head)), + } + } + // Have seen the first item, and there's something left. + init = self.peekable.fold(init, |acc, mut item| { + std::mem::swap(&mut head, &mut item); + f(acc, (Position::Middle, item)) + }); + // The "head" is now the last item. + init = f(init, (Position::Last, head)); + } + init + } } impl ExactSizeIterator for WithPosition where I: ExactSizeIterator {}