diff --git a/src/lib.rs b/src/lib.rs index 7dc40370b..ec1bbca2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1075,7 +1075,6 @@ pub trait Itertools: Iterator { where J: IntoIterator, F: FnMut(&Self::Item, &J::Item) -> T, - T: merge_join::OrderingOrBool, Self: Sized, { merge_join_by(self, other, cmp_fn) diff --git a/src/merge_join.rs b/src/merge_join.rs index 1769aaf3a..c83159186 100644 --- a/src/merge_join.rs +++ b/src/merge_join.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use std::fmt; -use std::iter::Fuse; -use std::iter::{FusedIterator, Peekable}; +use std::iter::{Fuse, FusedIterator}; +use std::marker::PhantomData; use either::Either; @@ -11,19 +11,9 @@ use crate::size_hint::{self, SizeHint}; #[cfg(doc)] use crate::Itertools; -pub trait MergePredicate { - fn merge_pred(&mut self, a: &T, b: &T) -> bool; -} - #[derive(Clone, Debug)] pub struct MergeLte; -impl MergePredicate for MergeLte { - fn merge_pred(&mut self, a: &T, b: &T) -> bool { - a <= b - } -} - /// An iterator adaptor that merges the two base iterators in ascending order. /// If both base iterators are sorted (ascending), the result is sorted. /// @@ -62,30 +52,10 @@ where /// /// See [`.merge_by()`](crate::Itertools::merge_by) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct MergeBy -where - I: Iterator, - J: Iterator, -{ - a: Peekable, - b: Peekable, - fused: Option, - cmp: F, -} - -impl fmt::Debug for MergeBy -where - I: Iterator + fmt::Debug, - J: Iterator + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(MergeBy, a, b); -} - -impl bool> MergePredicate for F { - fn merge_pred(&mut self, a: &T, b: &T) -> bool { - self(a, b) - } +pub struct MergeBy { + left: PutBack>, + right: PutBack>, + cmp_fn: F, } /// Create a `MergeBy` iterator. @@ -93,72 +63,14 @@ pub fn merge_by_new(a: I, b: J, cmp: F) -> MergeBy, - F: MergePredicate, { MergeBy { - a: a.into_iter().peekable(), - b: b.into_iter().peekable(), - fused: None, - cmp, + left: put_back(a.into_iter().fuse()), + right: put_back(b.into_iter().fuse()), + cmp_fn: cmp, } } -impl Clone for MergeBy -where - I: Iterator, - J: Iterator, - Peekable: Clone, - Peekable: Clone, - F: Clone, -{ - clone_fields!(a, b, fused, cmp); -} - -impl Iterator for MergeBy -where - I: Iterator, - J: Iterator, - F: MergePredicate, -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - let less_than = match self.fused { - Some(lt) => lt, - None => match (self.a.peek(), self.b.peek()) { - (Some(a), Some(b)) => self.cmp.merge_pred(a, b), - (Some(_), None) => { - self.fused = Some(true); - true - } - (None, Some(_)) => { - self.fused = Some(false); - false - } - (None, None) => return None, - }, - }; - if less_than { - self.a.next() - } else { - self.b.next() - } - } - - fn size_hint(&self) -> (usize, Option) { - // Not ExactSizeIterator because size may be larger than usize - size_hint::add(self.a.size_hint(), self.b.size_hint()) - } -} - -impl FusedIterator for MergeBy -where - I: FusedIterator, - J: FusedIterator, - F: MergePredicate, -{ -} - /// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order. /// /// [`IntoIterator`] enabled version of [`Itertools::merge_join_by`]. @@ -171,23 +83,29 @@ where I: IntoIterator, J: IntoIterator, F: FnMut(&I::Item, &J::Item) -> T, - T: OrderingOrBool, { - MergeJoinBy { + MergeBy { left: put_back(left.into_iter().fuse()), right: put_back(right.into_iter().fuse()), - cmp_fn, + cmp_fn: MergeFuncLR(cmp_fn, PhantomData), } } /// An iterator adaptor that merge-joins items from the two base iterators in ascending order. /// /// See [`.merge_join_by()`](crate::Itertools::merge_join_by) for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct MergeJoinBy { - left: PutBack>, - right: PutBack>, - cmp_fn: F, +pub type MergeJoinBy = + MergeBy::Item, ::Item>>::T>>; + +#[derive(Clone, Debug)] +pub struct MergeFuncLR(F, PhantomData); + +pub trait FuncLR { + type T; +} + +impl T> FuncLR for F { + type T = T; } pub trait OrderingOrBool { @@ -197,11 +115,11 @@ pub trait OrderingOrBool { // "merge" never returns (Some(...), Some(...), ...) so Option> // is appealing but it is always followed by two put_backs, so we think the compiler is // smart enough to optimize it. Or we could move put_backs into "merge". - fn merge(self, left: L, right: R) -> (Option, Option, Self::MergeResult); + fn merge(&mut self, left: L, right: R) -> (Option, Option, Self::MergeResult); fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint; } -impl OrderingOrBool for Ordering { +impl Ordering> OrderingOrBool for MergeFuncLR { type MergeResult = EitherOrBoth; fn left(left: L) -> Self::MergeResult { EitherOrBoth::Left(left) @@ -209,8 +127,8 @@ impl OrderingOrBool for Ordering { fn right(right: R) -> Self::MergeResult { EitherOrBoth::Right(right) } - fn merge(self, left: L, right: R) -> (Option, Option, Self::MergeResult) { - match self { + fn merge(&mut self, left: L, right: R) -> (Option, Option, Self::MergeResult) { + match self.0(&left, &right) { Ordering::Equal => (None, None, EitherOrBoth::Both(left, right)), Ordering::Less => (None, Some(right), EitherOrBoth::Left(left)), Ordering::Greater => (Some(left), None, EitherOrBoth::Right(right)), @@ -228,7 +146,7 @@ impl OrderingOrBool for Ordering { } } -impl OrderingOrBool for bool { +impl bool> OrderingOrBool for MergeFuncLR { type MergeResult = Either; fn left(left: L) -> Self::MergeResult { Either::Left(left) @@ -236,8 +154,8 @@ impl OrderingOrBool for bool { fn right(right: R) -> Self::MergeResult { Either::Right(right) } - fn merge(self, left: L, right: R) -> (Option, Option, Self::MergeResult) { - if self { + fn merge(&mut self, left: L, right: R) -> (Option, Option, Self::MergeResult) { + if self.0(&left, &right) { (None, Some(right), Either::Left(left)) } else { (Some(left), None, Either::Right(right)) @@ -249,7 +167,49 @@ impl OrderingOrBool for bool { } } -impl Clone for MergeJoinBy +impl bool> OrderingOrBool for F { + type MergeResult = T; + fn left(left: T) -> Self::MergeResult { + left + } + fn right(right: T) -> Self::MergeResult { + right + } + fn merge(&mut self, left: T, right: T) -> (Option, Option, Self::MergeResult) { + if self(&left, &right) { + (None, Some(right), left) + } else { + (Some(left), None, right) + } + } + fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { + // Not ExactSizeIterator because size may be larger than usize + size_hint::add(left, right) + } +} + +impl OrderingOrBool for MergeLte { + type MergeResult = T; + fn left(left: T) -> Self::MergeResult { + left + } + fn right(right: T) -> Self::MergeResult { + right + } + fn merge(&mut self, left: T, right: T) -> (Option, Option, Self::MergeResult) { + if left <= right { + (None, Some(right), left) + } else { + (Some(left), None, right) + } + } + fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint { + // Not ExactSizeIterator because size may be larger than usize + size_hint::add(left, right) + } +} + +impl Clone for MergeBy where I: Iterator, J: Iterator, @@ -260,32 +220,31 @@ where clone_fields!(left, right, cmp_fn); } -impl fmt::Debug for MergeJoinBy +impl fmt::Debug for MergeBy where I: Iterator + fmt::Debug, I::Item: fmt::Debug, J: Iterator + fmt::Debug, J::Item: fmt::Debug, { - debug_fmt_fields!(MergeJoinBy, left, right); + debug_fmt_fields!(MergeBy, left, right); } -impl Iterator for MergeJoinBy +impl Iterator for MergeBy where I: Iterator, J: Iterator, - F: FnMut(&I::Item, &J::Item) -> T, - T: OrderingOrBool, + F: OrderingOrBool, { - type Item = T::MergeResult; + type Item = F::MergeResult; fn next(&mut self) -> Option { match (self.left.next(), self.right.next()) { (None, None) => None, - (Some(left), None) => Some(T::left(left)), - (None, Some(right)) => Some(T::right(right)), + (Some(left), None) => Some(F::left(left)), + (None, Some(right)) => Some(F::right(right)), (Some(left), Some(right)) => { - let (left, right, next) = (self.cmp_fn)(&left, &right).merge(left, right); + let (left, right, next) = self.cmp_fn.merge(left, right); if let Some(left) = left { self.left.put_back(left); } @@ -298,7 +257,7 @@ where } fn size_hint(&self) -> SizeHint { - T::size_hint(self.left.size_hint(), self.right.size_hint()) + F::size_hint(self.left.size_hint(), self.right.size_hint()) } fn count(mut self) -> usize { @@ -310,7 +269,7 @@ where (None, Some(_right)) => break count + 1 + self.right.into_parts().1.count(), (Some(left), Some(right)) => { count += 1; - let (left, right, _) = (self.cmp_fn)(&left, &right).merge(left, right); + let (left, right, _) = self.cmp_fn.merge(left, right); if let Some(left) = left { self.left.put_back(left); } @@ -328,13 +287,13 @@ where match (self.left.next(), self.right.next()) { (None, None) => break previous_element, (Some(left), None) => { - break Some(T::left(self.left.into_parts().1.last().unwrap_or(left))) + break Some(F::left(self.left.into_parts().1.last().unwrap_or(left))) } (None, Some(right)) => { - break Some(T::right(self.right.into_parts().1.last().unwrap_or(right))) + break Some(F::right(self.right.into_parts().1.last().unwrap_or(right))) } (Some(left), Some(right)) => { - let (left, right, elem) = (self.cmp_fn)(&left, &right).merge(left, right); + let (left, right, elem) = self.cmp_fn.merge(left, right); if let Some(left) = left { self.left.put_back(left); } @@ -355,10 +314,10 @@ where n -= 1; match (self.left.next(), self.right.next()) { (None, None) => break None, - (Some(_left), None) => break self.left.nth(n).map(T::left), - (None, Some(_right)) => break self.right.nth(n).map(T::right), + (Some(_left), None) => break self.left.nth(n).map(F::left), + (None, Some(_right)) => break self.right.nth(n).map(F::right), (Some(left), Some(right)) => { - let (left, right, _) = (self.cmp_fn)(&left, &right).merge(left, right); + let (left, right, _) = self.cmp_fn.merge(left, right); if let Some(left) = left { self.left.put_back(left); } @@ -370,3 +329,11 @@ where } } } + +impl FusedIterator for MergeBy +where + I: Iterator, + J: Iterator, + F: OrderingOrBool, +{ +}