Skip to content

Make CoalesceBy lazy #801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 25, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 84 additions & 34 deletions src/adaptors/coalesce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,33 @@ use std::iter::FusedIterator;
use crate::size_hint;

#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct CoalesceBy<I, F, T>
pub struct CoalesceBy<I, F, C>
where
I: Iterator,
C: CountItem<I::Item>,
{
iter: I,
last: Option<T>,
/// `last` is `None` while no item have been taken out of `iter` (at definition).
/// Then `last` will be `Some(Some(item))` until `iter` is exhausted,
/// in which case `last` will be `Some(None)`.
last: Option<Option<C::CItem>>,
f: F,
}

impl<I: Clone, F: Clone, T: Clone> Clone for CoalesceBy<I, F, T>
impl<I: Clone, F: Clone, C> Clone for CoalesceBy<I, F, C>
where
I: Iterator,
C: CountItem<I::Item>,
C::CItem: Clone,
{
clone_fields!(last, iter, f);
}

impl<I, F, T> fmt::Debug for CoalesceBy<I, F, T>
impl<I, F, C> fmt::Debug for CoalesceBy<I, F, C>
where
I: Iterator + fmt::Debug,
T: fmt::Debug,
C: CountItem<I::Item>,
C::CItem: fmt::Debug,
{
debug_fmt_fields!(CoalesceBy, iter);
}
Expand All @@ -32,44 +39,56 @@ pub trait CoalescePredicate<Item, T> {
fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)>;
}

impl<I, F, T> Iterator for CoalesceBy<I, F, T>
impl<I, F, C> Iterator for CoalesceBy<I, F, C>
where
I: Iterator,
F: CoalescePredicate<I::Item, T>,
F: CoalescePredicate<I::Item, C::CItem>,
C: CountItem<I::Item>,
{
type Item = T;
type Item = C::CItem;

fn next(&mut self) -> Option<Self::Item> {
let Self { iter, last, f } = self;
// this fuses the iterator
let last = self.last.take()?;
let init = match last {
Some(elt) => elt.take(),
None => {
*last = Some(None);
iter.next().map(C::new)
}
}?;

let self_last = &mut self.last;
let self_f = &mut self.f;
Some(
self.iter
.try_fold(last, |last, next| match self_f.coalesce_pair(last, next) {
Ok(joined) => Ok(joined),
Err((last_, next_)) => {
*self_last = Some(next_);
Err(last_)
}
})
.unwrap_or_else(|x| x),
iter.try_fold(init, |accum, next| match f.coalesce_pair(accum, next) {
Ok(joined) => Ok(joined),
Err((last_, next_)) => {
*last = Some(Some(next_));
Err(last_)
}
})
.unwrap_or_else(|x| x),
)
}

fn size_hint(&self) -> (usize, Option<usize>) {
let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), self.last.is_some() as usize);
let (low, hi) = size_hint::add_scalar(
self.iter.size_hint(),
matches!(self.last, Some(Some(_))) as usize,
);
((low > 0) as usize, hi)
}

fn fold<Acc, FnAcc>(self, acc: Acc, mut fn_acc: FnAcc) -> Acc
where
FnAcc: FnMut(Acc, Self::Item) -> Acc,
{
if let Some(last) = self.last {
let mut f = self.f;
let (last, acc) = self.iter.fold((last, acc), |(last, acc), elt| {
let Self {
mut iter,
last,
mut f,
} = self;
if let Some(last) = last.unwrap_or_else(|| iter.next().map(C::new)) {
let (last, acc) = 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_)),
Expand All @@ -82,12 +101,43 @@ where
}
}

impl<I: Iterator, F: CoalescePredicate<I::Item, T>, T> FusedIterator for CoalesceBy<I, F, T> {}
impl<I, F, C> FusedIterator for CoalesceBy<I, F, C>
where
I: Iterator,
F: CoalescePredicate<I::Item, C::CItem>,
C: CountItem<I::Item>,
{
}

pub struct NoCount;

pub struct WithCount;

pub trait CountItem<T> {
type CItem;
fn new(t: T) -> Self::CItem;
}

impl<T> CountItem<T> for NoCount {
type CItem = T;
#[inline(always)]
fn new(t: T) -> T {
t
}
}

impl<T> CountItem<T> for WithCount {
type CItem = (usize, T);
#[inline(always)]
fn new(t: T) -> (usize, T) {
(1, t)
}
}

/// An iterator adaptor that may join together adjacent elements.
///
/// See [`.coalesce()`](crate::Itertools::coalesce) for more information.
pub type Coalesce<I, F> = CoalesceBy<I, F, <I as Iterator>::Item>;
pub type Coalesce<I, F> = CoalesceBy<I, F, NoCount>;

impl<F, Item, T> CoalescePredicate<Item, T> for F
where
Expand All @@ -99,12 +149,12 @@ where
}

/// Create a new `Coalesce`.
pub fn coalesce<I, F>(mut iter: I, f: F) -> Coalesce<I, F>
pub fn coalesce<I, F>(iter: I, f: F) -> Coalesce<I, F>
where
I: Iterator,
{
Coalesce {
last: iter.next(),
last: None,
iter,
f,
}
Expand All @@ -113,7 +163,7 @@ where
/// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function.
///
/// See [`.dedup_by()`](crate::Itertools::dedup_by) or [`.dedup()`](crate::Itertools::dedup) for more information.
pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, <I as Iterator>::Item>;
pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, NoCount>;

#[derive(Clone)]
pub struct DedupPred2CoalescePred<DP>(DP);
Expand Down Expand Up @@ -156,12 +206,12 @@ impl<T, F: FnMut(&T, &T) -> bool> DedupPredicate<T> for F {
}

/// Create a new `DedupBy`.
pub fn dedup_by<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupBy<I, Pred>
pub fn dedup_by<I, Pred>(iter: I, dedup_pred: Pred) -> DedupBy<I, Pred>
where
I: Iterator,
{
DedupBy {
last: iter.next(),
last: None,
iter,
f: DedupPred2CoalescePred(dedup_pred),
}
Expand All @@ -186,7 +236,7 @@ where
/// See [`.dedup_by_with_count()`](crate::Itertools::dedup_by_with_count) or
/// [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information.
pub type DedupByWithCount<I, Pred> =
CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, (usize, <I as Iterator>::Item)>;
CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, WithCount>;

#[derive(Clone, Debug)]
pub struct DedupPredWithCount2CoalescePred<DP>(DP);
Expand Down Expand Up @@ -215,12 +265,12 @@ where
pub type DedupWithCount<I> = DedupByWithCount<I, DedupEq>;

/// Create a new `DedupByWithCount`.
pub fn dedup_by_with_count<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupByWithCount<I, Pred>
pub fn dedup_by_with_count<I, Pred>(iter: I, dedup_pred: Pred) -> DedupByWithCount<I, Pred>
where
I: Iterator,
{
DedupByWithCount {
last: iter.next().map(|v| (1, v)),
last: None,
iter,
f: DedupPredWithCount2CoalescePred(dedup_pred),
}
Expand Down