From d353c9ff8f303e5f15cb6285c6bb36f7c606cb7a Mon Sep 17 00:00:00 2001 From: philipp Date: Sun, 17 May 2020 00:56:53 +0200 Subject: [PATCH 01/10] Add specialization `fn fold` to Coalesce (1) tests --- tests/quick.rs | 23 +++++++++++++++++++++++ tests/test_std.rs | 20 ++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/tests/quick.rs b/tests/quick.rs index f5f8d8f8f..d509c7929 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -753,6 +753,29 @@ quickcheck! { } } +quickcheck! { + fn dedup_via_coalesce(a: Vec) -> bool { + let mut b = a.clone(); + b.dedup(); + itertools::equal( + &b, + a + .iter() + .coalesce(|x, y| { + if x==y { + Ok(x) + } else { + Err((x, y)) + } + }) + .fold(vec![], |mut v, n| { + v.push(n); + v + }) + ) + } +} + quickcheck! { fn equal_dedup(a: Vec) -> bool { let mut b = a.clone(); diff --git a/tests/test_std.rs b/tests/test_std.rs index ec038fe47..9918592cb 100644 --- a/tests/test_std.rs +++ b/tests/test_std.rs @@ -109,6 +109,26 @@ fn dedup() { assert_eq!(&xs_d, &ys); } +#[test] +fn coalesce() { + let data = vec![-1., -2., -3., 3., 1., 0., -1.]; + let it = data.iter().cloned().coalesce(|x, y| + if (x >= 0.) == (y >= 0.) { + Ok(x + y) + } else { + Err((x, y)) + } + ); + itertools::assert_equal(it.clone(), vec![-6., 4., -1.]); + assert_eq!( + it.fold(vec![], |mut v, n| { + v.push(n); + v + }), + vec![-6., 4., -1.] + ); +} + #[test] fn dedup_by() { let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)]; From 6c6af09172cfa52719d89e8f7d3d26a1eccc467a Mon Sep 17 00:00:00 2001 From: philipp Date: Sun, 17 May 2020 16:21:30 +0200 Subject: [PATCH 02/10] Add specialization `fn fold` to Coalesce (2) --- src/adaptors/mod.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 8b100ed54..ac7b3933d 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -696,6 +696,23 @@ impl Iterator for Coalesce fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + + fn fold(self, acc: Acc, mut fn_acc: FnAcc) -> Acc + where FnAcc: FnMut(Acc, Self::Item) -> Acc, + { + if let Some(last) = self.iter.last { + let mut f = self.f; + let (last, acc) = self.iter.iter.fold((last, acc), |(last, acc), elt| { + match f(last, elt) { + Ok(joined) => (joined, acc), + Err((last_, next_)) => (next_, fn_acc(acc, last_)), + } + }); + fn_acc(acc, last) + } else { + acc + } + } } /// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function. From 642cbf009a31ef802176606f271083546ce3bc19 Mon Sep 17 00:00:00 2001 From: philipp Date: Sun, 17 May 2020 07:10:04 +0200 Subject: [PATCH 03/10] Introduce CoalescePredicate --- src/adaptors/mod.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index ac7b3933d..6f29e3dd7 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -656,6 +656,18 @@ pub struct Coalesce f: F, } +pub trait CoalescePredicate { + fn coalesce_pair(&mut self, t: T, item: Item) -> Result; +} + +impl CoalescePredicate for F + where F: FnMut(T, Item) -> Result +{ + fn coalesce_pair(&mut self, t: T, item: Item) -> Result { + self(t, item) + } +} + impl Clone for Coalesce where I: Iterator, I::Item: Clone @@ -685,12 +697,13 @@ pub fn coalesce(mut iter: I, f: F) -> Coalesce impl Iterator for Coalesce where I: Iterator, - F: FnMut(I::Item, I::Item) -> Result + F: CoalescePredicate { type Item = I::Item; fn next(&mut self) -> Option { - self.iter.next_with(&mut self.f) + let f = &mut self.f; + self.iter.next_with(|last, item| f.coalesce_pair(last, item)) } fn size_hint(&self) -> (usize, Option) { @@ -703,7 +716,7 @@ impl Iterator for Coalesce if let Some(last) = self.iter.last { let mut f = self.f; let (last, acc) = self.iter.iter.fold((last, acc), |(last, acc), elt| { - match f(last, elt) { + match f.coalesce_pair(last, elt) { Ok(joined) => (joined, acc), Err((last_, next_)) => (next_, fn_acc(acc, last_)), } From 7f2d4608d807bcc2da0ad87507c4979f1afdc82e Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 20 May 2020 19:43:53 +0200 Subject: [PATCH 04/10] Introduce CoalesceBy (1) Serves as base for Coalesce, will serve as base for Dedup et al. --- src/adaptors/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 6f29e3dd7..37d41e4fe 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -649,7 +649,9 @@ impl CoalesceCore /// /// See [`.coalesce()`](../trait.Itertools.html#method.coalesce) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Coalesce +pub type Coalesce = CoalesceBy; + +pub struct CoalesceBy where I: Iterator { iter: CoalesceCore, @@ -668,18 +670,18 @@ impl CoalescePredicate for F } } -impl Clone for Coalesce +impl Clone for CoalesceBy where I: Iterator, I::Item: Clone { clone_fields!(iter, f); } -impl fmt::Debug for Coalesce +impl fmt::Debug for CoalesceBy where I: Iterator + fmt::Debug, I::Item: fmt::Debug, { - debug_fmt_fields!(Coalesce, iter); + debug_fmt_fields!(CoalesceBy, iter); } /// Create a new `Coalesce`. @@ -695,7 +697,7 @@ pub fn coalesce(mut iter: I, f: F) -> Coalesce } } -impl Iterator for Coalesce +impl Iterator for CoalesceBy where I: Iterator, F: CoalescePredicate { From 0205bd5774f7c78a55bcad92fe91e86ccf81cec3 Mon Sep 17 00:00:00 2001 From: philipp Date: Sun, 17 May 2020 06:59:41 +0200 Subject: [PATCH 05/10] Introduce CoalesceBy (2) generalize it to coalesce to a different type Required for Dedup(By)WithCount. --- src/adaptors/mod.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 37d41e4fe..21dabc074 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -649,12 +649,12 @@ impl CoalesceCore /// /// See [`.coalesce()`](../trait.Itertools.html#method.coalesce) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub type Coalesce = CoalesceBy; +pub type Coalesce = CoalesceBy::Item>; -pub struct CoalesceBy +pub struct CoalesceBy where I: Iterator { - iter: CoalesceCore, + iter: CoalesceCore, f: F, } @@ -670,16 +670,15 @@ impl CoalescePredicate for F } } -impl Clone for CoalesceBy +impl Clone for CoalesceBy where I: Iterator, - I::Item: Clone { clone_fields!(iter, f); } -impl fmt::Debug for CoalesceBy +impl fmt::Debug for CoalesceBy where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, + T: fmt::Debug, { debug_fmt_fields!(CoalesceBy, iter); } @@ -697,11 +696,11 @@ pub fn coalesce(mut iter: I, f: F) -> Coalesce } } -impl Iterator for CoalesceBy +impl Iterator for CoalesceBy where I: Iterator, - F: CoalescePredicate + F: CoalescePredicate { - type Item = I::Item; + type Item = T; fn next(&mut self) -> Option { let f = &mut self.f; From 9d9bf4d233edbf6ba133c538ee7eb3567569f32b Mon Sep 17 00:00:00 2001 From: philipp Date: Sun, 17 May 2020 07:32:51 +0200 Subject: [PATCH 06/10] Implement DedupBy in terms of CoalesceBy --- src/adaptors/mod.rs | 71 ++++++++++----------------------------------- 1 file changed, 15 insertions(+), 56 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 21dabc074..9d08faf6c 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -9,7 +9,6 @@ mod multi_product; pub use self::multi_product::*; use std::fmt; -use std::mem::replace; use std::iter::{Fuse, Peekable, FromIterator, FusedIterator}; use std::marker::PhantomData; use crate::size_hint; @@ -733,11 +732,21 @@ impl Iterator for CoalesceBy /// /// See [`.dedup_by()`](../trait.Itertools.html#method.dedup_by) or [`.dedup()`](../trait.Itertools.html#method.dedup) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct DedupBy - where I: Iterator +pub type DedupBy = CoalesceBy, ::Item>; + +#[derive(Clone)] +pub struct DedupPred2CoalescePred(DP); + +impl CoalescePredicate for DedupPred2CoalescePred + where DP: DedupPredicate { - iter: CoalesceCore, - dedup_pred: Pred, + fn coalesce_pair(&mut self, t: T, item: T) -> Result { + if self.0.dedup_pair(&t, &item) { + Ok(t) + } else { + Err((t, item)) + } + } } pub trait DedupPredicate { // TODO replace by Fn(&T, &T)->bool once Rust supports it @@ -764,13 +773,6 @@ implbool> DedupPredicate for F { /// See [`.dedup()`](../trait.Itertools.html#method.dedup) for more information. pub type Dedup=DedupBy; -impl Clone for DedupBy - where I: Iterator, - I::Item: Clone, -{ - clone_fields!(iter, dedup_pred); -} - /// Create a new `DedupBy`. pub fn dedup_by(mut iter: I, dedup_pred: Pred) -> DedupBy where I: Iterator, @@ -780,7 +782,7 @@ pub fn dedup_by(mut iter: I, dedup_pred: Pred) -> DedupBy last: iter.next(), iter, }, - dedup_pred, + f: DedupPred2CoalescePred(dedup_pred), } } @@ -791,49 +793,6 @@ pub fn dedup(iter: I) -> Dedup dedup_by(iter, DedupEq) } -impl fmt::Debug for DedupBy - where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(Dedup, iter); -} - -impl Iterator for DedupBy - where I: Iterator, - Pred: DedupPredicate, -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - let ref mut dedup_pred = self.dedup_pred; - self.iter.next_with(|x, y| { - if dedup_pred.dedup_pair(&x, &y) { Ok(x) } else { Err((x, y)) } - }) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - fn fold(self, mut accum: Acc, mut f: G) -> Acc - where G: FnMut(Acc, Self::Item) -> Acc, - { - if let Some(mut last) = self.iter.last { - let mut dedup_pred = self.dedup_pred; - accum = self.iter.iter.fold(accum, |acc, elt| { - if dedup_pred.dedup_pair(&elt, &last) { - acc - } else { - f(acc, replace(&mut last, elt)) - } - }); - f(accum, last) - } else { - accum - } - } -} - /// An iterator adaptor that removes repeated duplicates, while keeping a count of how many /// repeated elements were present. This will determine equality using a comparison function. /// From 5d7f32eb645a228d3d211fe8f14f173cb74f3bf7 Mon Sep 17 00:00:00 2001 From: philipp Date: Sun, 17 May 2020 07:32:51 +0200 Subject: [PATCH 07/10] Implement DedupByWithCount in terms of CoalesceBy --- src/adaptors/mod.rs | 52 +++++++++++++-------------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 9d08faf6c..139945efc 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -799,11 +799,21 @@ pub fn dedup(iter: I) -> Dedup /// See [`.dedup_by_with_count()`](../trait.Itertools.html#method.dedup_by_with_count) or /// [`.dedup_with_count()`](../trait.Itertools.html#method.dedup_with_count) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct DedupByWithCount - where I: Iterator +pub type DedupByWithCount = CoalesceBy, (usize, ::Item)>; + +#[derive(Clone)] +pub struct DedupPredWithCount2CoalescePred(DP); + +impl CoalescePredicate for DedupPredWithCount2CoalescePred + where DP: DedupPredicate { - iter: CoalesceCore, - dedup_pred: Pred, + fn coalesce_pair(&mut self, (c, t): (usize, T), item: T) -> Result<(usize, T), ((usize, T), (usize, T))> { + if self.0.dedup_pair(&t, &item) { + Ok((c + 1, t)) + } else { + Err(((c, t), (1, item))) + } + } } /// An iterator adaptor that removes repeated duplicates, while keeping a count of how many @@ -821,7 +831,7 @@ pub fn dedup_by_with_count(mut iter: I, dedup_pred: Pred) -> DedupByWit last: iter.next().map(|v| (1, v)), iter, }, - dedup_pred, + f: DedupPredWithCount2CoalescePred(dedup_pred), } } @@ -832,38 +842,6 @@ pub fn dedup_with_count(iter: I) -> DedupWithCount dedup_by_with_count(iter, DedupEq) } -impl fmt::Debug for DedupByWithCount - where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(Dedup, iter); -} - -impl Clone for DedupByWithCount - where I: Iterator, - I::Item: Clone, -{ - clone_fields!(iter, dedup_pred); -} - -impl Iterator for DedupByWithCount - where I: Iterator, - Pred: DedupPredicate, -{ - type Item = (usize, I::Item); - - fn next(&mut self) -> Option<(usize, I::Item)> { - let ref mut dedup_pred = self.dedup_pred; - self.iter.next_with(|(c, x), y| { - if dedup_pred.dedup_pair(&x, &y) { Ok((c + 1, x)) } else { Err(((c, x), (1, y))) } - }) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - impl> FusedIterator for DedupByWithCount {} /// An iterator adaptor that borrows from a `Clone`-able iterator From a36ed869f988ebc8515c7aa825b4d762940474e2 Mon Sep 17 00:00:00 2001 From: philipp Date: Tue, 19 May 2020 19:17:43 +0200 Subject: [PATCH 08/10] Inline CoalesceCore into CoalesceBy (1) fields --- src/adaptors/mod.rs | 50 ++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 139945efc..b7d377d28 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -605,19 +605,11 @@ impl Iterator for MergeBy } } -#[derive(Clone, Debug)] -pub struct CoalesceCore - where I: Iterator -{ - iter: I, - last: Option, -} - -impl CoalesceCore +impl CoalesceBy where I: Iterator { - fn next_with(&mut self, mut f: F) -> Option - where F: FnMut(T, I::Item) -> Result + fn next_with(&mut self) -> Option + where F: CoalescePredicate { // this fuses the iterator let mut last = match self.last.take() { @@ -625,7 +617,7 @@ impl CoalesceCore Some(x) => x, }; for next in &mut self.iter { - match f(last, next) { + match self.f.coalesce_pair(last, next) { Ok(joined) => last = joined, Err((last_, next_)) => { self.last = Some(next_); @@ -637,7 +629,7 @@ impl CoalesceCore Some(last) } - fn size_hint(&self) -> (usize, Option) { + fn size_hint_internal(&self) -> (usize, Option) { let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), self.last.is_some() as usize); ((low > 0) as usize, hi) @@ -653,7 +645,8 @@ pub type Coalesce = CoalesceBy::Item>; pub struct CoalesceBy where I: Iterator { - iter: CoalesceCore, + iter: I, + last: Option, f: F, } @@ -672,7 +665,7 @@ impl CoalescePredicate for F impl Clone for CoalesceBy where I: Iterator, { - clone_fields!(iter, f); + clone_fields!(last, iter, f); } impl fmt::Debug for CoalesceBy @@ -687,10 +680,8 @@ pub fn coalesce(mut iter: I, f: F) -> Coalesce where I: Iterator { Coalesce { - iter: CoalesceCore { - last: iter.next(), - iter, - }, + last: iter.next(), + iter, f, } } @@ -702,20 +693,19 @@ impl Iterator for CoalesceBy type Item = T; fn next(&mut self) -> Option { - let f = &mut self.f; - self.iter.next_with(|last, item| f.coalesce_pair(last, item)) + self.next_with() } fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() + self.size_hint_internal() } fn fold(self, acc: Acc, mut fn_acc: FnAcc) -> Acc where FnAcc: FnMut(Acc, Self::Item) -> Acc, { - if let Some(last) = self.iter.last { + if let Some(last) = self.last { let mut f = self.f; - let (last, acc) = self.iter.iter.fold((last, acc), |(last, acc), elt| { + let (last, acc) = self.iter.fold((last, acc), |(last, acc), elt| { match f.coalesce_pair(last, elt) { Ok(joined) => (joined, acc), Err((last_, next_)) => (next_, fn_acc(acc, last_)), @@ -778,10 +768,8 @@ pub fn dedup_by(mut iter: I, dedup_pred: Pred) -> DedupBy where I: Iterator, { DedupBy { - iter: CoalesceCore { - last: iter.next(), - iter, - }, + last: iter.next(), + iter, f: DedupPred2CoalescePred(dedup_pred), } } @@ -827,10 +815,8 @@ pub fn dedup_by_with_count(mut iter: I, dedup_pred: Pred) -> DedupByWit where I: Iterator, { DedupByWithCount { - iter: CoalesceCore { - last: iter.next().map(|v| (1, v)), - iter, - }, + last: iter.next().map(|v| (1, v)), + iter, f: DedupPredWithCount2CoalescePred(dedup_pred), } } From 2a246b94c6f80635e3256d5e00e7649d493976f7 Mon Sep 17 00:00:00 2001 From: philipp Date: Tue, 19 May 2020 19:17:43 +0200 Subject: [PATCH 09/10] Inline CoalesceCore into CoalesceBy (2) impl --- src/adaptors/mod.rs | 51 ++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index b7d377d28..d2738984a 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -605,37 +605,6 @@ impl Iterator for MergeBy } } -impl CoalesceBy - where I: Iterator -{ - fn next_with(&mut self) -> Option - where F: CoalescePredicate - { - // this fuses the iterator - let mut last = match self.last.take() { - None => return None, - Some(x) => x, - }; - for next in &mut self.iter { - match self.f.coalesce_pair(last, next) { - Ok(joined) => last = joined, - Err((last_, next_)) => { - self.last = Some(next_); - return Some(last_); - } - } - } - - Some(last) - } - - fn size_hint_internal(&self) -> (usize, Option) { - let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), - self.last.is_some() as usize); - ((low > 0) as usize, hi) - } -} - /// An iterator adaptor that may join together adjacent elements. /// /// See [`.coalesce()`](../trait.Itertools.html#method.coalesce) for more information. @@ -693,11 +662,27 @@ impl Iterator for CoalesceBy type Item = T; fn next(&mut self) -> Option { - self.next_with() + // this fuses the iterator + let mut last = match self.last.take() { + None => return None, + Some(x) => x, + }; + for next in &mut self.iter { + match self.f.coalesce_pair(last, next) { + Ok(joined) => last = joined, + Err((last_, next_)) => { + self.last = Some(next_); + return Some(last_); + } + } + } + Some(last) } fn size_hint(&self) -> (usize, Option) { - self.size_hint_internal() + let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), + self.last.is_some() as usize); + ((low > 0) as usize, hi) } fn fold(self, acc: Acc, mut fn_acc: FnAcc) -> Acc From dc8b5becec470d4f68b5952c999bb27de86c4a10 Mon Sep 17 00:00:00 2001 From: philipp Date: Sun, 24 May 2020 08:53:04 +0200 Subject: [PATCH 10/10] Implement FusedIterator on CoalesceBy --- src/adaptors/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index d2738984a..7da8a3c58 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -703,6 +703,8 @@ impl Iterator for CoalesceBy } } +impl, T> FusedIterator for CoalesceBy {} + /// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function. /// /// See [`.dedup_by()`](../trait.Itertools.html#method.dedup_by) or [`.dedup()`](../trait.Itertools.html#method.dedup) for more information. @@ -813,8 +815,6 @@ pub fn dedup_with_count(iter: I) -> DedupWithCount dedup_by_with_count(iter, DedupEq) } -impl> FusedIterator for DedupByWithCount {} - /// An iterator adaptor that borrows from a `Clone`-able iterator /// to only pick off elements while the predicate returns `true`. ///