Skip to content

Commit 5f56ce5

Browse files
committed
Update Merge and MergeBy: avoid function pointers
Refactor common parts of merge into MergeCore For issue #49
1 parent 6ead6ae commit 5f56ce5

File tree

4 files changed

+135
-71
lines changed

4 files changed

+135
-71
lines changed

benches/bench1.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use itertools::ZipSlices;
1414

1515
use std::iter::repeat;
1616
use std::cmp;
17-
use std::cmp::Ordering;
1817

1918
#[bench]
2019
fn slice_iter(b: &mut test::Bencher)
@@ -609,7 +608,7 @@ fn merge_by_cmp(b: &mut test::Bencher) {
609608
let data1 = test::black_box(data1);
610609
let data2 = test::black_box(data2);
611610
b.iter(|| {
612-
data1.iter().merge_by(&data2, Ord::cmp).count()
611+
data1.iter().merge_by(&data2, PartialOrd::le).count()
613612
})
614613
}
615614

@@ -635,6 +634,6 @@ fn merge_by_lt(b: &mut test::Bencher) {
635634
let data1 = test::black_box(data1);
636635
let data2 = test::black_box(data2);
637636
b.iter(|| {
638-
data1.iter().merge_by(&data2, |a, b| if a <= b { Ordering::Less } else { Ordering::Greater }).count()
637+
data1.iter().merge_by(&data2, |a, b| a <= b).count()
639638
})
640639
}

src/adaptors.rs

Lines changed: 124 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use std::mem;
1010
use std::num::One;
1111
#[cfg(feature = "unstable")]
1212
use std::ops::Add;
13-
use std::cmp::Ordering;
1413
use std::iter::{Fuse, Peekable};
1514
use std::collections::HashSet;
1615
use std::hash::Hash;
@@ -504,80 +503,39 @@ impl<I> ExactSizeIterator for Step<I> where
504503
I: ExactSizeIterator,
505504
{ }
506505

507-
/// An iterator adaptor that merges the two base iterators in ascending order.
508-
/// If both base iterators are sorted (ascending), the result is sorted.
509-
///
510-
/// Iterator element type is `I::Item`.
511-
///
512-
/// See [*.merge_by()*](trait.Itertools.html#method.merge_by) for more information.
513-
pub struct Merge<I, J, F> where
514-
I: Iterator,
515-
J: Iterator<Item=I::Item>,
506+
507+
struct MergeCore<I, J>
508+
where I: Iterator,
509+
J: Iterator<Item=I::Item>,
516510
{
517511
a: Peekable<I>,
518512
b: Peekable<J>,
519-
cmp: F,
520513
fused: Option<bool>,
521514
}
522515

523-
// default ordering function for .merge()
524-
// Note: To be replaced by unit struct
525-
#[inline]
526-
pub fn merge_default_ordering<A: PartialOrd>(a: &A, b: &A) -> Ordering {
527-
if a > b {
528-
Ordering::Greater
529-
} else {
530-
Ordering::Less
531-
}
532-
}
533-
534-
impl<I, J, F> Merge<I, J, F> where
535-
I: Iterator,
536-
J: Iterator<Item=I::Item>,
537-
F: FnMut(&I::Item, &I::Item) -> Ordering
538-
{
539-
/// Create a `Merge` iterator.
540-
pub fn new(a: I, b: J, cmp: F) -> Self
541-
{
542-
Merge {
543-
a: a.peekable(),
544-
b: b.peekable(),
545-
cmp: cmp,
546-
fused: None,
547-
}
548-
}
549-
}
550516

551-
impl<I, J, F> Clone for Merge<I, J, F> where
517+
impl<I, J> Clone for MergeCore<I, J> where
552518
I: Iterator,
553519
J: Iterator<Item=I::Item>,
554520
Peekable<I>: Clone,
555521
Peekable<J>: Clone,
556-
F: Clone,
557522
{
558523
fn clone(&self) -> Self {
559-
clone_fields!(Merge, self, a, b, cmp, fused)
524+
clone_fields!(MergeCore, self, a, b, fused)
560525
}
561526
}
562527

563-
impl<I, J, F> Iterator for Merge<I, J, F> where
564-
I: Iterator,
565-
J: Iterator<Item=I::Item>,
566-
F: FnMut(&I::Item, &I::Item) -> Ordering
528+
impl<I, J> MergeCore<I, J>
529+
where I: Iterator,
530+
J: Iterator<Item=I::Item>,
567531
{
568-
type Item = I::Item;
569-
570-
fn next(&mut self) -> Option<I::Item> {
532+
fn next_with<F>(&mut self, mut less_than: F) -> Option<I::Item>
533+
where F: FnMut(&I::Item, &I::Item) -> bool
534+
{
571535
let less_than = match self.fused {
572536
Some(lt) => lt,
573537
None => match (self.a.peek(), self.b.peek()) {
574-
(Some(a), Some(b)) => {
575-
match (self.cmp)(a, b) {
576-
Ordering::Less => true,
577-
Ordering::Equal => true,
578-
Ordering::Greater => false,
579-
}
580-
}
538+
(Some(a), Some(b)) => less_than(a, b),
581539
(Some(_), None) => {
582540
self.fused = Some(true);
583541
true
@@ -603,6 +561,117 @@ impl<I, J, F> Iterator for Merge<I, J, F> where
603561
}
604562
}
605563

564+
/// An iterator adaptor that merges the two base iterators in ascending order.
565+
/// If both base iterators are sorted (ascending), the result is sorted.
566+
///
567+
/// Iterator element type is `I::Item`.
568+
///
569+
/// See [*.merge()*](trait.Itertools.html#method.merge_by) for more information.
570+
pub struct Merge<I, J> where
571+
I: Iterator,
572+
J: Iterator<Item=I::Item>,
573+
{
574+
merge: MergeCore<I, J>,
575+
}
576+
577+
impl<I, J> Clone for Merge<I, J> where
578+
I: Iterator,
579+
J: Iterator<Item=I::Item>,
580+
Peekable<I>: Clone,
581+
Peekable<J>: Clone,
582+
{
583+
fn clone(&self) -> Self {
584+
clone_fields!(Merge, self, merge)
585+
}
586+
}
587+
588+
/// Create a `Merge` iterator.
589+
pub fn merge_new<I, J>(a: I, b: J) -> Merge<I, J>
590+
where I: Iterator,
591+
J: Iterator<Item=I::Item>,
592+
{
593+
Merge {
594+
merge: MergeCore {
595+
a: a.peekable(),
596+
b: b.peekable(),
597+
fused: None,
598+
}
599+
}
600+
}
601+
602+
impl<I, J> Iterator for Merge<I, J>
603+
where I: Iterator,
604+
J: Iterator<Item=I::Item>,
605+
I::Item: PartialOrd,
606+
{
607+
type Item = I::Item;
608+
609+
fn next(&mut self) -> Option<I::Item> {
610+
self.merge.next_with(|a, b| a <= b)
611+
}
612+
613+
fn size_hint(&self) -> (usize, Option<usize>) {
614+
self.merge.size_hint()
615+
}
616+
}
617+
618+
/// An iterator adaptor that merges the two base iterators in ascending order.
619+
/// If both base iterators are sorted (ascending), the result is sorted.
620+
///
621+
/// Iterator element type is `I::Item`.
622+
///
623+
/// See [*.merge_by()*](trait.Itertools.html#method.merge_by) for more information.
624+
pub struct MergeBy<I, J, F> where
625+
I: Iterator,
626+
J: Iterator<Item=I::Item>,
627+
{
628+
merge: MergeCore<I, J>,
629+
cmp: F,
630+
}
631+
632+
/// Create a `MergeBy` iterator.
633+
pub fn merge_by_new<I, J, F>(a: I, b: J, cmp: F) -> MergeBy<I, J, F>
634+
where I: Iterator,
635+
J: Iterator<Item=I::Item>,
636+
{
637+
MergeBy {
638+
merge: MergeCore {
639+
a: a.peekable(),
640+
b: b.peekable(),
641+
fused: None,
642+
},
643+
cmp: cmp,
644+
}
645+
}
646+
647+
impl<I, J, F> Clone for MergeBy<I, J, F> where
648+
I: Iterator,
649+
J: Iterator<Item=I::Item>,
650+
Peekable<I>: Clone,
651+
Peekable<J>: Clone,
652+
F: Clone,
653+
{
654+
fn clone(&self) -> Self {
655+
clone_fields!(MergeBy, self, merge, cmp)
656+
}
657+
}
658+
659+
impl<I, J, F> Iterator for MergeBy<I, J, F> where
660+
I: Iterator,
661+
J: Iterator<Item=I::Item>,
662+
F: FnMut(&I::Item, &I::Item) -> bool
663+
{
664+
type Item = I::Item;
665+
666+
fn next(&mut self) -> Option<I::Item> {
667+
self.merge.next_with(&mut self.cmp)
668+
}
669+
670+
fn size_hint(&self) -> (usize, Option<usize>) {
671+
self.merge.size_hint()
672+
}
673+
}
674+
606675
#[cfg(feature = "unstable")]
607676
/// An iterator adaptor that enumerates the iterator elements,
608677
/// with a custom starting value and integer type.
@@ -787,8 +856,6 @@ impl<I, F> Iterator for Coalesce<I, F> where
787856
}
788857
}
789858

790-
791-
792859
/// An iterator adaptor that borrows from a `Clone`-able iterator
793860
/// to only pick off elements while the predicate returns `true`.
794861
///

src/lib.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ pub use adaptors::{
5151
GroupBy,
5252
Step,
5353
Merge,
54+
MergeBy,
5455
MultiPeek,
5556
TakeWhileRef,
5657
WhileSome,
@@ -105,9 +106,6 @@ mod zipslices;
105106
/// The function pointer map iterator created with `.map_fn()`.
106107
pub type MapFn<I, B> where I: Iterator = iter::Map<I, fn(I::Item) -> B>;
107108

108-
/// An ascending order merge iterator created with `.merge()`.
109-
pub type MergeAscend<I, J> where I: Iterator = Merge<I, J, fn(&I::Item, &I::Item) -> Ordering>;
110-
111109
#[macro_export]
112110
/// Create an iterator over the “cartesian product” of iterators.
113111
///
@@ -521,12 +519,12 @@ pub trait Itertools : Iterator {
521519
/// let it = a.merge(b);
522520
/// itertools::assert_equal(it, vec![0, 0, 3, 5, 6, 9, 10]);
523521
/// ```
524-
fn merge<J>(self, other: J) -> MergeAscend<Self, J::IntoIter> where
522+
fn merge<J>(self, other: J) -> Merge<Self, J::IntoIter> where
525523
Self: Sized,
526524
Self::Item: PartialOrd,
527525
J: IntoIterator<Item=Self::Item>,
528526
{
529-
self.merge_by(other, adaptors::merge_default_ordering)
527+
adaptors::merge_new(self, other.into_iter())
530528
}
531529

532530
/// Return an iterator adaptor that merges the two base iterators in order.
@@ -541,16 +539,16 @@ pub trait Itertools : Iterator {
541539
///
542540
/// let a = (0..).zip("bc".chars());
543541
/// let b = (0..).zip("ad".chars());
544-
/// let it = a.merge_by(b, |x, y| Ord::cmp(&x.1, &y.1));
542+
/// let it = a.merge_by(b, |x, y| x.1 <= y.1);
545543
/// itertools::assert_equal(it, vec![(0, 'a'), (0, 'b'), (1, 'c'), (1, 'd')]);
546544
/// ```
547545
548-
fn merge_by<J, F>(self, other: J, cmp: F) -> Merge<Self, J::IntoIter, F> where
546+
fn merge_by<J, F>(self, other: J, is_first: F) -> MergeBy<Self, J::IntoIter, F> where
549547
Self: Sized,
550548
J: IntoIterator<Item=Self::Item>,
551-
F: FnMut(&Self::Item, &Self::Item) -> Ordering
549+
F: FnMut(&Self::Item, &Self::Item) -> bool
552550
{
553-
Merge::new(self, other.into_iter(), cmp)
551+
adaptors::merge_by_new(self, other.into_iter(), is_first)
554552
}
555553

556554
/// Return an iterator adaptor that iterates over the cartesian product of

tests/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ fn merge_by() {
372372
let odd : Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")];
373373
let even = vec![(2, "foo"), (4, "bar"), (6, "baz")];
374374
let expected = vec![(1, "hello"), (2, "foo"), (3, "world"), (4, "bar"), (5, "!"), (6, "baz")];
375-
let results = odd.iter().merge_by(even.iter(), |a, b|{ a.0.cmp(&b.0)});
375+
let results = odd.iter().merge_by(even.iter(), |a, b| a.0 <= b.0);
376376
it::assert_equal(results, expected.iter());
377377
}
378378

@@ -385,7 +385,7 @@ fn merge_by_btree() {
385385
let mut bt2 = BTreeMap::new();
386386
bt2.insert("foo", 2);
387387
bt2.insert("bar", 4);
388-
let results = bt1.into_iter().merge_by(bt2.into_iter(), |a, b|{a.0.cmp(&b.0)});
388+
let results = bt1.into_iter().merge_by(bt2.into_iter(), |a, b| a.0 <= b.0 );
389389
let expected = vec![("bar", 4), ("foo", 2), ("hello", 1), ("world", 3)];
390390
it::assert_equal(results, expected.into_iter());
391391
}

0 commit comments

Comments
 (0)