Skip to content

Commit a517cfc

Browse files
bors[bot]phimuemue
andauthored
Merge #440
440: Implement DedupBy in terms of Coalesce r=jswrenn a=phimuemue During the discussion of #423 (comment), we found that `DedupBy`/`DedupByWithCount` could actually be implemented in terms of `Coalesce`. This simplifies the implementation quite a bit and lets derived adaptors inherit specializations. * Implement `fold` on `Coalesce` so that all the adaptors derived from it inherit the specialization (currently, only `DedupBy` has this specialization). * Introduce `CoalescePredicate` (similar to `DedupPredicate`) to allow for different customizations of `Coalesce`. * Introduce parametrizable `CoalesceBy`: Base for `Coalesce`, `DedupBy`, `DedupByWithCount`. * Implement `DedupBy`/`DedupByWithCount` in terms of `CoalesceBy` (using particular `impl`s). * At last, the indirection through `CoalesceCore` is not needed anymore. Co-authored-by: philipp <[email protected]>
2 parents 7a97808 + dc8b5be commit a517cfc

File tree

3 files changed

+139
-157
lines changed

3 files changed

+139
-157
lines changed

src/adaptors/mod.rs

Lines changed: 96 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ mod multi_product;
99
pub use self::multi_product::*;
1010

1111
use std::fmt;
12-
use std::mem::replace;
1312
use std::iter::{Fuse, Peekable, FromIterator, FusedIterator};
1413
use std::marker::PhantomData;
1514
use crate::size_hint;
@@ -606,107 +605,125 @@ impl<I, J, F> Iterator for MergeBy<I, J, F>
606605
}
607606
}
608607

609-
#[derive(Clone, Debug)]
610-
pub struct CoalesceCore<I, T>
608+
/// An iterator adaptor that may join together adjacent elements.
609+
///
610+
/// See [`.coalesce()`](../trait.Itertools.html#method.coalesce) for more information.
611+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
612+
pub type Coalesce<I, F> = CoalesceBy<I, F, <I as Iterator>::Item>;
613+
614+
pub struct CoalesceBy<I, F, T>
611615
where I: Iterator
612616
{
613617
iter: I,
614618
last: Option<T>,
619+
f: F,
615620
}
616621

617-
impl<I, T> CoalesceCore<I, T>
618-
where I: Iterator
619-
{
620-
fn next_with<F>(&mut self, mut f: F) -> Option<T>
621-
where F: FnMut(T, I::Item) -> Result<T, (T, T)>
622-
{
623-
// this fuses the iterator
624-
let mut last = match self.last.take() {
625-
None => return None,
626-
Some(x) => x,
627-
};
628-
for next in &mut self.iter {
629-
match f(last, next) {
630-
Ok(joined) => last = joined,
631-
Err((last_, next_)) => {
632-
self.last = Some(next_);
633-
return Some(last_);
634-
}
635-
}
636-
}
637-
638-
Some(last)
639-
}
640-
641-
fn size_hint(&self) -> (usize, Option<usize>) {
642-
let (low, hi) = size_hint::add_scalar(self.iter.size_hint(),
643-
self.last.is_some() as usize);
644-
((low > 0) as usize, hi)
645-
}
622+
pub trait CoalescePredicate<Item, T> {
623+
fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)>;
646624
}
647625

648-
/// An iterator adaptor that may join together adjacent elements.
649-
///
650-
/// See [`.coalesce()`](../trait.Itertools.html#method.coalesce) for more information.
651-
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
652-
pub struct Coalesce<I, F>
653-
where I: Iterator
626+
impl<F, Item, T> CoalescePredicate<Item, T> for F
627+
where F: FnMut(T, Item) -> Result<T, (T, T)>
654628
{
655-
iter: CoalesceCore<I, I::Item>,
656-
f: F,
629+
fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)> {
630+
self(t, item)
631+
}
657632
}
658633

659-
impl<I: Clone, F: Clone> Clone for Coalesce<I, F>
634+
impl<I: Clone, F: Clone, T: Clone> Clone for CoalesceBy<I, F, T>
660635
where I: Iterator,
661-
I::Item: Clone
662636
{
663-
clone_fields!(iter, f);
637+
clone_fields!(last, iter, f);
664638
}
665639

666-
impl<I, F> fmt::Debug for Coalesce<I, F>
640+
impl<I, F, T> fmt::Debug for CoalesceBy<I, F, T>
667641
where I: Iterator + fmt::Debug,
668-
I::Item: fmt::Debug,
642+
T: fmt::Debug,
669643
{
670-
debug_fmt_fields!(Coalesce, iter);
644+
debug_fmt_fields!(CoalesceBy, iter);
671645
}
672646

673647
/// Create a new `Coalesce`.
674648
pub fn coalesce<I, F>(mut iter: I, f: F) -> Coalesce<I, F>
675649
where I: Iterator
676650
{
677651
Coalesce {
678-
iter: CoalesceCore {
679-
last: iter.next(),
680-
iter,
681-
},
652+
last: iter.next(),
653+
iter,
682654
f,
683655
}
684656
}
685657

686-
impl<I, F> Iterator for Coalesce<I, F>
658+
impl<I, F, T> Iterator for CoalesceBy<I, F, T>
687659
where I: Iterator,
688-
F: FnMut(I::Item, I::Item) -> Result<I::Item, (I::Item, I::Item)>
660+
F: CoalescePredicate<I::Item, T>
689661
{
690-
type Item = I::Item;
662+
type Item = T;
691663

692664
fn next(&mut self) -> Option<Self::Item> {
693-
self.iter.next_with(&mut self.f)
665+
// this fuses the iterator
666+
let mut last = match self.last.take() {
667+
None => return None,
668+
Some(x) => x,
669+
};
670+
for next in &mut self.iter {
671+
match self.f.coalesce_pair(last, next) {
672+
Ok(joined) => last = joined,
673+
Err((last_, next_)) => {
674+
self.last = Some(next_);
675+
return Some(last_);
676+
}
677+
}
678+
}
679+
Some(last)
694680
}
695681

696682
fn size_hint(&self) -> (usize, Option<usize>) {
697-
self.iter.size_hint()
683+
let (low, hi) = size_hint::add_scalar(self.iter.size_hint(),
684+
self.last.is_some() as usize);
685+
((low > 0) as usize, hi)
686+
}
687+
688+
fn fold<Acc, FnAcc>(self, acc: Acc, mut fn_acc: FnAcc) -> Acc
689+
where FnAcc: FnMut(Acc, Self::Item) -> Acc,
690+
{
691+
if let Some(last) = self.last {
692+
let mut f = self.f;
693+
let (last, acc) = self.iter.fold((last, acc), |(last, acc), elt| {
694+
match f.coalesce_pair(last, elt) {
695+
Ok(joined) => (joined, acc),
696+
Err((last_, next_)) => (next_, fn_acc(acc, last_)),
697+
}
698+
});
699+
fn_acc(acc, last)
700+
} else {
701+
acc
702+
}
698703
}
699704
}
700705

706+
impl<I: Iterator, F: CoalescePredicate<I::Item, T>, T> FusedIterator for CoalesceBy<I, F, T> {}
707+
701708
/// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function.
702709
///
703710
/// See [`.dedup_by()`](../trait.Itertools.html#method.dedup_by) or [`.dedup()`](../trait.Itertools.html#method.dedup) for more information.
704711
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
705-
pub struct DedupBy<I, Pred>
706-
where I: Iterator
712+
pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, <I as Iterator>::Item>;
713+
714+
#[derive(Clone)]
715+
pub struct DedupPred2CoalescePred<DP>(DP);
716+
717+
impl<DP, T> CoalescePredicate<T, T> for DedupPred2CoalescePred<DP>
718+
where DP: DedupPredicate<T>
707719
{
708-
iter: CoalesceCore<I, I::Item>,
709-
dedup_pred: Pred,
720+
fn coalesce_pair(&mut self, t: T, item: T) -> Result<T, (T, T)> {
721+
if self.0.dedup_pair(&t, &item) {
722+
Ok(t)
723+
} else {
724+
Err((t, item))
725+
}
726+
}
710727
}
711728

712729
pub trait DedupPredicate<T> { // TODO replace by Fn(&T, &T)->bool once Rust supports it
@@ -733,23 +750,14 @@ impl<T, F: FnMut(&T, &T)->bool> DedupPredicate<T> for F {
733750
/// See [`.dedup()`](../trait.Itertools.html#method.dedup) for more information.
734751
pub type Dedup<I>=DedupBy<I, DedupEq>;
735752

736-
impl<I: Clone, Pred: Clone> Clone for DedupBy<I, Pred>
737-
where I: Iterator,
738-
I::Item: Clone,
739-
{
740-
clone_fields!(iter, dedup_pred);
741-
}
742-
743753
/// Create a new `DedupBy`.
744754
pub fn dedup_by<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupBy<I, Pred>
745755
where I: Iterator,
746756
{
747757
DedupBy {
748-
iter: CoalesceCore {
749-
last: iter.next(),
750-
iter,
751-
},
752-
dedup_pred,
758+
last: iter.next(),
759+
iter,
760+
f: DedupPred2CoalescePred(dedup_pred),
753761
}
754762
}
755763

@@ -760,60 +768,27 @@ pub fn dedup<I>(iter: I) -> Dedup<I>
760768
dedup_by(iter, DedupEq)
761769
}
762770

763-
impl<I, Pred> fmt::Debug for DedupBy<I, Pred>
764-
where I: Iterator + fmt::Debug,
765-
I::Item: fmt::Debug,
766-
{
767-
debug_fmt_fields!(Dedup, iter);
768-
}
769-
770-
impl<I, Pred> Iterator for DedupBy<I, Pred>
771-
where I: Iterator,
772-
Pred: DedupPredicate<I::Item>,
773-
{
774-
type Item = I::Item;
775-
776-
fn next(&mut self) -> Option<Self::Item> {
777-
let ref mut dedup_pred = self.dedup_pred;
778-
self.iter.next_with(|x, y| {
779-
if dedup_pred.dedup_pair(&x, &y) { Ok(x) } else { Err((x, y)) }
780-
})
781-
}
782-
783-
fn size_hint(&self) -> (usize, Option<usize>) {
784-
self.iter.size_hint()
785-
}
786-
787-
fn fold<Acc, G>(self, mut accum: Acc, mut f: G) -> Acc
788-
where G: FnMut(Acc, Self::Item) -> Acc,
789-
{
790-
if let Some(mut last) = self.iter.last {
791-
let mut dedup_pred = self.dedup_pred;
792-
accum = self.iter.iter.fold(accum, |acc, elt| {
793-
if dedup_pred.dedup_pair(&elt, &last) {
794-
acc
795-
} else {
796-
f(acc, replace(&mut last, elt))
797-
}
798-
});
799-
f(accum, last)
800-
} else {
801-
accum
802-
}
803-
}
804-
}
805-
806771
/// An iterator adaptor that removes repeated duplicates, while keeping a count of how many
807772
/// repeated elements were present. This will determine equality using a comparison function.
808773
///
809774
/// See [`.dedup_by_with_count()`](../trait.Itertools.html#method.dedup_by_with_count) or
810775
/// [`.dedup_with_count()`](../trait.Itertools.html#method.dedup_with_count) for more information.
811776
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
812-
pub struct DedupByWithCount<I, Pred>
813-
where I: Iterator
777+
pub type DedupByWithCount<I, Pred> = CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, (usize, <I as Iterator>::Item)>;
778+
779+
#[derive(Clone)]
780+
pub struct DedupPredWithCount2CoalescePred<DP>(DP);
781+
782+
impl<DP, T> CoalescePredicate<T, (usize, T)> for DedupPredWithCount2CoalescePred<DP>
783+
where DP: DedupPredicate<T>
814784
{
815-
iter: CoalesceCore<I, (usize, I::Item)>,
816-
dedup_pred: Pred,
785+
fn coalesce_pair(&mut self, (c, t): (usize, T), item: T) -> Result<(usize, T), ((usize, T), (usize, T))> {
786+
if self.0.dedup_pair(&t, &item) {
787+
Ok((c + 1, t))
788+
} else {
789+
Err(((c, t), (1, item)))
790+
}
791+
}
817792
}
818793

819794
/// An iterator adaptor that removes repeated duplicates, while keeping a count of how many
@@ -827,11 +802,9 @@ pub fn dedup_by_with_count<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupByWit
827802
where I: Iterator,
828803
{
829804
DedupByWithCount {
830-
iter: CoalesceCore {
831-
last: iter.next().map(|v| (1, v)),
832-
iter,
833-
},
834-
dedup_pred,
805+
last: iter.next().map(|v| (1, v)),
806+
iter,
807+
f: DedupPredWithCount2CoalescePred(dedup_pred),
835808
}
836809
}
837810

@@ -842,40 +815,6 @@ pub fn dedup_with_count<I>(iter: I) -> DedupWithCount<I>
842815
dedup_by_with_count(iter, DedupEq)
843816
}
844817

845-
impl<I, Pred> fmt::Debug for DedupByWithCount<I, Pred>
846-
where I: Iterator + fmt::Debug,
847-
I::Item: fmt::Debug,
848-
{
849-
debug_fmt_fields!(Dedup, iter);
850-
}
851-
852-
impl<I: Clone, Pred: Clone> Clone for DedupByWithCount<I, Pred>
853-
where I: Iterator,
854-
I::Item: Clone,
855-
{
856-
clone_fields!(iter, dedup_pred);
857-
}
858-
859-
impl<I, Pred> Iterator for DedupByWithCount<I, Pred>
860-
where I: Iterator,
861-
Pred: DedupPredicate<I::Item>,
862-
{
863-
type Item = (usize, I::Item);
864-
865-
fn next(&mut self) -> Option<(usize, I::Item)> {
866-
let ref mut dedup_pred = self.dedup_pred;
867-
self.iter.next_with(|(c, x), y| {
868-
if dedup_pred.dedup_pair(&x, &y) { Ok((c + 1, x)) } else { Err(((c, x), (1, y))) }
869-
})
870-
}
871-
872-
fn size_hint(&self) -> (usize, Option<usize>) {
873-
self.iter.size_hint()
874-
}
875-
}
876-
877-
impl<I: Iterator, Pred: DedupPredicate<I::Item>> FusedIterator for DedupByWithCount<I, Pred> {}
878-
879818
/// An iterator adaptor that borrows from a `Clone`-able iterator
880819
/// to only pick off elements while the predicate returns `true`.
881820
///

tests/quick.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,29 @@ quickcheck! {
753753
}
754754
}
755755

756+
quickcheck! {
757+
fn dedup_via_coalesce(a: Vec<i32>) -> bool {
758+
let mut b = a.clone();
759+
b.dedup();
760+
itertools::equal(
761+
&b,
762+
a
763+
.iter()
764+
.coalesce(|x, y| {
765+
if x==y {
766+
Ok(x)
767+
} else {
768+
Err((x, y))
769+
}
770+
})
771+
.fold(vec![], |mut v, n| {
772+
v.push(n);
773+
v
774+
})
775+
)
776+
}
777+
}
778+
756779
quickcheck! {
757780
fn equal_dedup(a: Vec<i32>) -> bool {
758781
let mut b = a.clone();

0 commit comments

Comments
 (0)