forked from rust-itertools/itertools
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5ad6f70
commit d853808
Showing
2 changed files
with
143 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
use std::fmt; | ||
use std::iter::{Peekable, FusedIterator}; | ||
|
||
use crate::size_hint; | ||
|
||
pub trait MergePredicate<T> { | ||
fn merge_pred(&mut self, a: &T, b: &T) -> bool; | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct MergeLte; | ||
|
||
impl<T: PartialOrd> MergePredicate<T> 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. | ||
/// | ||
/// Iterator element type is `I::Item`. | ||
/// | ||
/// See [`.merge()`](crate::Itertools::merge_by) for more information. | ||
pub type Merge<I, J> = MergeBy<I, J, MergeLte>; | ||
|
||
/// Create an iterator that merges elements in `i` and `j`. | ||
/// | ||
/// [`IntoIterator`] enabled version of [`Itertools::merge`](crate::Itertools::merge). | ||
/// | ||
/// ``` | ||
/// use itertools::merge; | ||
/// | ||
/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) { | ||
/// /* loop body */ | ||
/// } | ||
/// ``` | ||
pub fn merge<I, J>(i: I, j: J) -> Merge<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter> | ||
where I: IntoIterator, | ||
J: IntoIterator<Item = I::Item>, | ||
I::Item: PartialOrd | ||
{ | ||
merge_by_new(i, j, MergeLte) | ||
} | ||
|
||
/// An iterator adaptor that merges the two base iterators in ascending order. | ||
/// If both base iterators are sorted (ascending), the result is sorted. | ||
/// | ||
/// Iterator element type is `I::Item`. | ||
/// | ||
/// See [`.merge_by()`](crate::Itertools::merge_by) for more information. | ||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] | ||
pub struct MergeBy<I, J, F> | ||
where I: Iterator, | ||
J: Iterator<Item = I::Item> | ||
{ | ||
a: Peekable<I>, | ||
b: Peekable<J>, | ||
fused: Option<bool>, | ||
cmp: F, | ||
} | ||
|
||
impl<I, J, F> fmt::Debug for MergeBy<I, J, F> | ||
where I: Iterator + fmt::Debug, J: Iterator<Item = I::Item> + fmt::Debug, | ||
I::Item: fmt::Debug, | ||
{ | ||
debug_fmt_fields!(MergeBy, a, b); | ||
} | ||
|
||
impl<T, F: FnMut(&T, &T) -> bool> MergePredicate<T> for F { | ||
fn merge_pred(&mut self, a: &T, b: &T) -> bool { | ||
self(a, b) | ||
} | ||
} | ||
|
||
/// Create a `MergeBy` iterator. | ||
pub fn merge_by_new<I, J, F>(a: I, b: J, cmp: F) -> MergeBy<I::IntoIter, J::IntoIter, F> | ||
where I: IntoIterator, | ||
J: IntoIterator<Item = I::Item>, | ||
F: MergePredicate<I::Item>, | ||
{ | ||
MergeBy { | ||
a: a.into_iter().peekable(), | ||
b: b.into_iter().peekable(), | ||
fused: None, | ||
cmp, | ||
} | ||
} | ||
|
||
impl<I, J, F> Clone for MergeBy<I, J, F> | ||
where I: Iterator, | ||
J: Iterator<Item = I::Item>, | ||
Peekable<I>: Clone, | ||
Peekable<J>: Clone, | ||
F: Clone | ||
{ | ||
clone_fields!(a, b, fused, cmp); | ||
} | ||
|
||
impl<I, J, F> Iterator for MergeBy<I, J, F> | ||
where I: Iterator, | ||
J: Iterator<Item = I::Item>, | ||
F: MergePredicate<I::Item> | ||
{ | ||
type Item = I::Item; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
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<usize>) { | ||
// Not ExactSizeIterator because size may be larger than usize | ||
size_hint::add(self.a.size_hint(), self.b.size_hint()) | ||
} | ||
} | ||
|
||
impl<I, J, F> FusedIterator for MergeBy<I, J, F> | ||
where I: FusedIterator, | ||
J: FusedIterator<Item = I::Item>, | ||
F: MergePredicate<I::Item> | ||
{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters