Skip to content

Commit 73feddb

Browse files
authoredMar 13, 2019
Rollup merge of #59138 - timvermeulen:simplify_select_fold1, r=sfackler
Simplify Iterator::{min, max} This PR simplifies the `select_fold1` helper method used to implmement `Iterator::{min, min_by, min_by_key, max, max_by, max_by_key}` by removing the projection argument, which was only used by the implementations of `min_by_key` and `max_by_key`. I also added tests to ensure that the stability as mentioned in the comments of `min` and `max` is preserved, and fixed the `iter::{bench_max, bench_max_by_key}` benchmarks which the compiler presumably was able to collapse into closed-form expressions. None of the benchmark results were impacted, I suspect their generated assembly didn't change.
2 parents c22566d + 6cc5a3d commit 73feddb

File tree

3 files changed

+49
-58
lines changed

3 files changed

+49
-58
lines changed
 

‎src/libcore/benches/iter.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fn scatter(x: i32) -> i32 { (x * 31) % 127 }
3535
fn bench_max_by_key(b: &mut Bencher) {
3636
b.iter(|| {
3737
let it = 0..100;
38-
it.max_by_key(|&x| scatter(x))
38+
it.map(black_box).max_by_key(|&x| scatter(x))
3939
})
4040
}
4141

@@ -56,7 +56,7 @@ fn bench_max_by_key2(b: &mut Bencher) {
5656
fn bench_max(b: &mut Bencher) {
5757
b.iter(|| {
5858
let it = 0..100;
59-
it.map(scatter).max()
59+
it.map(black_box).map(scatter).max()
6060
})
6161
}
6262

‎src/libcore/iter/traits/iterator.rs

+19-56
Original file line numberDiff line numberDiff line change
@@ -2008,12 +2008,7 @@ pub trait Iterator {
20082008
#[stable(feature = "rust1", since = "1.0.0")]
20092009
fn max(self) -> Option<Self::Item> where Self: Sized, Self::Item: Ord
20102010
{
2011-
select_fold1(self,
2012-
|_| (),
2013-
// switch to y even if it is only equal, to preserve
2014-
// stability.
2015-
|_, x, _, y| *x <= *y)
2016-
.map(|(_, x)| x)
2011+
self.max_by(Ord::cmp)
20172012
}
20182013

20192014
/// Returns the minimum element of an iterator.
@@ -2038,12 +2033,7 @@ pub trait Iterator {
20382033
#[stable(feature = "rust1", since = "1.0.0")]
20392034
fn min(self) -> Option<Self::Item> where Self: Sized, Self::Item: Ord
20402035
{
2041-
select_fold1(self,
2042-
|_| (),
2043-
// only switch to y if it is strictly smaller, to
2044-
// preserve stability.
2045-
|_, x, _, y| *x > *y)
2046-
.map(|(_, x)| x)
2036+
self.min_by(Ord::cmp)
20472037
}
20482038

20492039
/// Returns the element that gives the maximum value from the
@@ -2062,15 +2052,11 @@ pub trait Iterator {
20622052
/// ```
20632053
#[inline]
20642054
#[stable(feature = "iter_cmp_by_key", since = "1.6.0")]
2065-
fn max_by_key<B: Ord, F>(self, f: F) -> Option<Self::Item>
2055+
fn max_by_key<B: Ord, F>(self, mut f: F) -> Option<Self::Item>
20662056
where Self: Sized, F: FnMut(&Self::Item) -> B,
20672057
{
2068-
select_fold1(self,
2069-
f,
2070-
// switch to y even if it is only equal, to preserve
2071-
// stability.
2072-
|x_p, _, y_p, _| x_p <= y_p)
2073-
.map(|(_, x)| x)
2058+
// switch to y even if it is only equal, to preserve stability.
2059+
select_fold1(self.map(|x| (f(&x), x)), |(x_p, _), (y_p, _)| x_p <= y_p).map(|(_, x)| x)
20742060
}
20752061

20762062
/// Returns the element that gives the maximum value with respect to the
@@ -2092,12 +2078,8 @@ pub trait Iterator {
20922078
fn max_by<F>(self, mut compare: F) -> Option<Self::Item>
20932079
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering,
20942080
{
2095-
select_fold1(self,
2096-
|_| (),
2097-
// switch to y even if it is only equal, to preserve
2098-
// stability.
2099-
|_, x, _, y| Ordering::Greater != compare(x, y))
2100-
.map(|(_, x)| x)
2081+
// switch to y even if it is only equal, to preserve stability.
2082+
select_fold1(self, |x, y| compare(x, y) != Ordering::Greater)
21012083
}
21022084

21032085
/// Returns the element that gives the minimum value from the
@@ -2115,15 +2097,11 @@ pub trait Iterator {
21152097
/// assert_eq!(*a.iter().min_by_key(|x| x.abs()).unwrap(), 0);
21162098
/// ```
21172099
#[stable(feature = "iter_cmp_by_key", since = "1.6.0")]
2118-
fn min_by_key<B: Ord, F>(self, f: F) -> Option<Self::Item>
2100+
fn min_by_key<B: Ord, F>(self, mut f: F) -> Option<Self::Item>
21192101
where Self: Sized, F: FnMut(&Self::Item) -> B,
21202102
{
2121-
select_fold1(self,
2122-
f,
2123-
// only switch to y if it is strictly smaller, to
2124-
// preserve stability.
2125-
|x_p, _, y_p, _| x_p > y_p)
2126-
.map(|(_, x)| x)
2103+
// only switch to y if it is strictly smaller, to preserve stability.
2104+
select_fold1(self.map(|x| (f(&x), x)), |(x_p, _), (y_p, _)| x_p > y_p).map(|(_, x)| x)
21272105
}
21282106

21292107
/// Returns the element that gives the minimum value with respect to the
@@ -2145,12 +2123,8 @@ pub trait Iterator {
21452123
fn min_by<F>(self, mut compare: F) -> Option<Self::Item>
21462124
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering,
21472125
{
2148-
select_fold1(self,
2149-
|_| (),
2150-
// switch to y even if it is strictly smaller, to
2151-
// preserve stability.
2152-
|_, x, _, y| Ordering::Greater == compare(x, y))
2153-
.map(|(_, x)| x)
2126+
// only switch to y if it is strictly smaller, to preserve stability.
2127+
select_fold1(self, |x, y| compare(x, y) == Ordering::Greater)
21542128
}
21552129

21562130

@@ -2693,34 +2667,23 @@ pub trait Iterator {
26932667
}
26942668
}
26952669

2696-
/// Select an element from an iterator based on the given "projection"
2697-
/// and "comparison" function.
2670+
/// Select an element from an iterator based on the given "comparison"
2671+
/// function.
26982672
///
26992673
/// This is an idiosyncratic helper to try to factor out the
27002674
/// commonalities of {max,min}{,_by}. In particular, this avoids
27012675
/// having to implement optimizations several times.
27022676
#[inline]
2703-
fn select_fold1<I, B, FProj, FCmp>(mut it: I,
2704-
mut f_proj: FProj,
2705-
mut f_cmp: FCmp) -> Option<(B, I::Item)>
2706-
where I: Iterator,
2707-
FProj: FnMut(&I::Item) -> B,
2708-
FCmp: FnMut(&B, &I::Item, &B, &I::Item) -> bool
2677+
fn select_fold1<I, F>(mut it: I, mut f: F) -> Option<I::Item>
2678+
where
2679+
I: Iterator,
2680+
F: FnMut(&I::Item, &I::Item) -> bool,
27092681
{
27102682
// start with the first element as our selection. This avoids
27112683
// having to use `Option`s inside the loop, translating to a
27122684
// sizeable performance gain (6x in one case).
27132685
it.next().map(|first| {
2714-
let first_p = f_proj(&first);
2715-
2716-
it.fold((first_p, first), |(sel_p, sel), x| {
2717-
let x_p = f_proj(&x);
2718-
if f_cmp(&sel_p, &sel, &x_p, &x) {
2719-
(x_p, x)
2720-
} else {
2721-
(sel_p, sel)
2722-
}
2723-
})
2686+
it.fold(first, |sel, x| if f(&sel, &x) { x } else { sel })
27242687
})
27252688
}
27262689

‎src/libcore/tests/iter.rs

+28
Original file line numberDiff line numberDiff line change
@@ -1082,12 +1082,39 @@ fn test_iterator_product_result() {
10821082
assert_eq!(v.iter().cloned().product::<Result<i32, _>>(), Err(()));
10831083
}
10841084

1085+
/// A wrapper struct that implements `Eq` and `Ord` based on the wrapped
1086+
/// integer modulo 3. Used to test that `Iterator::max` and `Iterator::min`
1087+
/// return the correct element if some of them are equal.
1088+
#[derive(Debug)]
1089+
struct Mod3(i32);
1090+
1091+
impl PartialEq for Mod3 {
1092+
fn eq(&self, other: &Self) -> bool {
1093+
self.0 % 3 == other.0 % 3
1094+
}
1095+
}
1096+
1097+
impl Eq for Mod3 {}
1098+
1099+
impl PartialOrd for Mod3 {
1100+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
1101+
Some(self.cmp(other))
1102+
}
1103+
}
1104+
1105+
impl Ord for Mod3 {
1106+
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
1107+
(self.0 % 3).cmp(&(other.0 % 3))
1108+
}
1109+
}
1110+
10851111
#[test]
10861112
fn test_iterator_max() {
10871113
let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
10881114
assert_eq!(v[..4].iter().cloned().max(), Some(3));
10891115
assert_eq!(v.iter().cloned().max(), Some(10));
10901116
assert_eq!(v[..0].iter().cloned().max(), None);
1117+
assert_eq!(v.iter().cloned().map(Mod3).max().map(|x| x.0), Some(8));
10911118
}
10921119

10931120
#[test]
@@ -1096,6 +1123,7 @@ fn test_iterator_min() {
10961123
assert_eq!(v[..4].iter().cloned().min(), Some(0));
10971124
assert_eq!(v.iter().cloned().min(), Some(0));
10981125
assert_eq!(v[..0].iter().cloned().min(), None);
1126+
assert_eq!(v.iter().cloned().map(Mod3).min().map(|x| x.0), Some(0));
10991127
}
11001128

11011129
#[test]

0 commit comments

Comments
 (0)
Please sign in to comment.