Skip to content

Commit b14b20c

Browse files
authored
Rollup merge of rust-lang#62278 - cuviper:iter-partition, r=alexcrichton
Add Iterator::partition_in_place() and is_partitioned() `partition_in_place()` swaps `&mut T` items in-place to satisfy the predicate, so all `true` items precede all `false` items. This requires a `DoubleEndedIterator` so we can search from front and back for items that need swapping. `is_partitioned()` checks whether the predicate is already satisfied.
2 parents 3c3e375 + 7171c83 commit b14b20c

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed

src/libcore/iter/traits/iterator.rs

+100
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,11 @@ pub trait Iterator {
14721472
/// `partition()` returns a pair, all of the elements for which it returned
14731473
/// `true`, and all of the elements for which it returned `false`.
14741474
///
1475+
/// See also [`is_partitioned()`] and [`partition_in_place()`].
1476+
///
1477+
/// [`is_partitioned()`]: #method.is_partitioned
1478+
/// [`partition_in_place()`]: #method.partition_in_place
1479+
///
14751480
/// # Examples
14761481
///
14771482
/// Basic usage:
@@ -1506,6 +1511,101 @@ pub trait Iterator {
15061511
(left, right)
15071512
}
15081513

1514+
/// Reorder the elements of this iterator *in-place* according to the given predicate,
1515+
/// such that all those that return `true` precede all those that return `false`.
1516+
/// Returns the number of `true` elements found.
1517+
///
1518+
/// The relative order of partitioned items is not maintained.
1519+
///
1520+
/// See also [`is_partitioned()`] and [`partition()`].
1521+
///
1522+
/// [`is_partitioned()`]: #method.is_partitioned
1523+
/// [`partition()`]: #method.partition
1524+
///
1525+
/// # Examples
1526+
///
1527+
/// ```
1528+
/// #![feature(iter_partition_in_place)]
1529+
///
1530+
/// let mut a = [1, 2, 3, 4, 5, 6, 7];
1531+
///
1532+
/// // Partition in-place between evens and odds
1533+
/// let i = a.iter_mut().partition_in_place(|&n| n % 2 == 0);
1534+
///
1535+
/// assert_eq!(i, 3);
1536+
/// assert!(a[..i].iter().all(|&n| n % 2 == 0)); // evens
1537+
/// assert!(a[i..].iter().all(|&n| n % 2 == 1)); // odds
1538+
/// ```
1539+
#[unstable(feature = "iter_partition_in_place", reason = "new API", issue = "62543")]
1540+
fn partition_in_place<'a, T: 'a, P>(mut self, ref mut predicate: P) -> usize
1541+
where
1542+
Self: Sized + DoubleEndedIterator<Item = &'a mut T>,
1543+
P: FnMut(&T) -> bool,
1544+
{
1545+
// FIXME: should we worry about the count overflowing? The only way to have more than
1546+
// `usize::MAX` mutable references is with ZSTs, which aren't useful to partition...
1547+
1548+
// These closure "factory" functions exist to avoid genericity in `Self`.
1549+
1550+
#[inline]
1551+
fn is_false<'a, T>(
1552+
predicate: &'a mut impl FnMut(&T) -> bool,
1553+
true_count: &'a mut usize,
1554+
) -> impl FnMut(&&mut T) -> bool + 'a {
1555+
move |x| {
1556+
let p = predicate(&**x);
1557+
*true_count += p as usize;
1558+
!p
1559+
}
1560+
}
1561+
1562+
#[inline]
1563+
fn is_true<T>(
1564+
predicate: &mut impl FnMut(&T) -> bool
1565+
) -> impl FnMut(&&mut T) -> bool + '_ {
1566+
move |x| predicate(&**x)
1567+
}
1568+
1569+
// Repeatedly find the first `false` and swap it with the last `true`.
1570+
let mut true_count = 0;
1571+
while let Some(head) = self.find(is_false(predicate, &mut true_count)) {
1572+
if let Some(tail) = self.rfind(is_true(predicate)) {
1573+
crate::mem::swap(head, tail);
1574+
true_count += 1;
1575+
} else {
1576+
break;
1577+
}
1578+
}
1579+
true_count
1580+
}
1581+
1582+
/// Checks if the elements of this iterator are partitioned according to the given predicate,
1583+
/// such that all those that return `true` precede all those that return `false`.
1584+
///
1585+
/// See also [`partition()`] and [`partition_in_place()`].
1586+
///
1587+
/// [`partition()`]: #method.partition
1588+
/// [`partition_in_place()`]: #method.partition_in_place
1589+
///
1590+
/// # Examples
1591+
///
1592+
/// ```
1593+
/// #![feature(iter_is_partitioned)]
1594+
///
1595+
/// assert!("Iterator".chars().is_partitioned(char::is_uppercase));
1596+
/// assert!(!"IntoIterator".chars().is_partitioned(char::is_uppercase));
1597+
/// ```
1598+
#[unstable(feature = "iter_is_partitioned", reason = "new API", issue = "62544")]
1599+
fn is_partitioned<P>(mut self, mut predicate: P) -> bool
1600+
where
1601+
Self: Sized,
1602+
P: FnMut(Self::Item) -> bool,
1603+
{
1604+
// Either all items test `true`, or the first clause stops at `false`
1605+
// and we check that there are no more `true` items after that.
1606+
self.all(&mut predicate) || !self.any(predicate)
1607+
}
1608+
15091609
/// An iterator method that applies a function as long as it returns
15101610
/// successfully, producing a single, final value.
15111611
///

src/libcore/tests/iter.rs

+36
Original file line numberDiff line numberDiff line change
@@ -2460,3 +2460,39 @@ fn test_is_sorted() {
24602460
assert!(!["c", "bb", "aaa"].iter().is_sorted());
24612461
assert!(["c", "bb", "aaa"].iter().is_sorted_by_key(|s| s.len()));
24622462
}
2463+
2464+
#[test]
2465+
fn test_partition() {
2466+
fn check(xs: &mut [i32], ref p: impl Fn(&i32) -> bool, expected: usize) {
2467+
let i = xs.iter_mut().partition_in_place(p);
2468+
assert_eq!(expected, i);
2469+
assert!(xs[..i].iter().all(p));
2470+
assert!(!xs[i..].iter().any(p));
2471+
assert!(xs.iter().is_partitioned(p));
2472+
if i == 0 || i == xs.len() {
2473+
assert!(xs.iter().rev().is_partitioned(p));
2474+
} else {
2475+
assert!(!xs.iter().rev().is_partitioned(p));
2476+
}
2477+
}
2478+
2479+
check(&mut [], |_| true, 0);
2480+
check(&mut [], |_| false, 0);
2481+
2482+
check(&mut [0], |_| true, 1);
2483+
check(&mut [0], |_| false, 0);
2484+
2485+
check(&mut [-1, 1], |&x| x > 0, 1);
2486+
check(&mut [-1, 1], |&x| x < 0, 1);
2487+
2488+
let ref mut xs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
2489+
check(xs, |_| true, 10);
2490+
check(xs, |_| false, 0);
2491+
check(xs, |&x| x % 2 == 0, 5); // evens
2492+
check(xs, |&x| x % 2 == 1, 5); // odds
2493+
check(xs, |&x| x % 3 == 0, 4); // multiple of 3
2494+
check(xs, |&x| x % 4 == 0, 3); // multiple of 4
2495+
check(xs, |&x| x % 5 == 0, 2); // multiple of 5
2496+
check(xs, |&x| x < 3, 3); // small
2497+
check(xs, |&x| x > 6, 3); // large
2498+
}

src/libcore/tests/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#![feature(slice_partition_dedup)]
3232
#![feature(int_error_matching)]
3333
#![feature(const_fn)]
34+
#![feature(iter_partition_in_place)]
35+
#![feature(iter_is_partitioned)]
3436
#![warn(rust_2018_idioms)]
3537

3638
extern crate test;

0 commit comments

Comments
 (0)