Skip to content

Commit 8fbb588

Browse files
seanchen1991cuviper
authored andcommitted
Add find_map variants
Add non-working test for find_map_first Got find_map_first test compiling Get first test passing Remove unused file Need to write more comprehensive tests Add tests for find_map_any Add last newline back to find_first_last
1 parent b4553ab commit 8fbb588

File tree

3 files changed

+139
-2
lines changed

3 files changed

+139
-2
lines changed

src/iter/mod.rs

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1504,7 +1504,7 @@ pub trait ParallelIterator: Sized + Send {
15041504
/// just want the first match that discovered anywhere in the iterator,
15051505
/// `find_any` is a better choice.
15061506
///
1507-
/// # Exmaples
1507+
/// # Examples
15081508
///
15091509
/// ```
15101510
/// use rayon::prelude::*;
@@ -1551,6 +1551,99 @@ pub trait ParallelIterator: Sized + Send {
15511551
find_first_last::find_last(self, predicate)
15521552
}
15531553

1554+
/// Applies the given predicate to the items in the parallel iterator
1555+
/// and returns **any** non-None result of the map operation.
1556+
///
1557+
/// Once a non-None value is produced from the map operation, we will
1558+
/// attempt to stop processing the rest of the items in the iterator
1559+
/// as soon as possible.
1560+
///
1561+
/// Note that this method only returns **some** item in the parallel
1562+
/// iterator that is not None from the map predicate. The item returned
1563+
/// may not be the **first** non-None value produced in the parallel
1564+
/// sequence, since the entire sequence is mapped over in parallel.
1565+
///
1566+
/// # Examples
1567+
///
1568+
/// ```
1569+
/// use rayon::prelude::*;
1570+
///
1571+
/// let c = ["lol", "NaN", "5", "5"];
1572+
///
1573+
/// let first_number = c.par_iter().find_map_first(|s| s.parse().ok());
1574+
///
1575+
/// assert_eq!(first_number, Some(5));
1576+
/// ```
1577+
fn find_map_any<P, R>(self, predicate: P) -> Option<R>
1578+
where
1579+
P: Fn(Self::Item) -> Option<R> + Sync + Send,
1580+
R: Send,
1581+
{
1582+
self.filter_map(predicate).find_any(|_| true)
1583+
}
1584+
1585+
/// Applies the given predicate to the items in the parallel iterator and
1586+
/// returns the sequentially **first** non-None result of the map operation.
1587+
///
1588+
/// Once a non-None value is produced from the map operation, all attempts
1589+
/// to the right of the match will be stopped, while attempts to the left
1590+
/// must continue in case an earlier match is found.
1591+
///
1592+
/// Note that not all parallel iterators have a useful order, much like
1593+
/// sequential `HashMap` iteration, so "first" may be nebulous. If you
1594+
/// just want the first non-None value discovered anywhere in the iterator,
1595+
/// `find_map_any` is a better choice.
1596+
///
1597+
/// # Examples
1598+
///
1599+
/// ```
1600+
/// use rayon::prelude::*;
1601+
///
1602+
/// let c = ["lol", "NaN", "2", "5"];
1603+
///
1604+
/// let first_number = c.par_iter().find_map_first(|s| s.parse().ok());
1605+
///
1606+
/// assert_eq!(first_number, Some(2));
1607+
/// ```
1608+
fn find_map_first<P, R>(self, predicate: P) -> Option<R>
1609+
where
1610+
P: Fn(Self::Item) -> Option<R> + Sync + Send,
1611+
R: Send,
1612+
{
1613+
self.filter_map(predicate).find_first(|_| true)
1614+
}
1615+
1616+
/// Applies the given predicate to the items in the parallel iterator and
1617+
/// returns the sequentially **last** non-None result of the map operation.
1618+
///
1619+
/// Once a non-None value is produced from the map operation, all attempts
1620+
/// to the left of the match will be stopped, while attempts to the right
1621+
/// must continue in case a later match is found.
1622+
///
1623+
/// Note that not all parallel iterators have a useful order, much like
1624+
/// sequential `HashMap` iteration, so "first" may be nebulous. If you
1625+
/// just want the first non-None value discovered anywhere in the iterator,
1626+
/// `find_map_any` is a better choice.
1627+
///
1628+
/// # Examples
1629+
///
1630+
/// ```
1631+
/// use rayon::prelude::*;
1632+
///
1633+
/// let c = ["lol", "NaN", "2", "5"];
1634+
///
1635+
/// let first_number = c.par_iter().find_map_last(|s| s.parse().ok());
1636+
///
1637+
/// assert_eq!(first_number, Some(5));
1638+
/// ```
1639+
fn find_map_last<P, R>(self, predicate: P) -> Option<R>
1640+
where
1641+
P: Fn(Self::Item) -> Option<R> + Sync + Send,
1642+
R: Send,
1643+
{
1644+
self.filter_map(predicate).find_last(|_| true)
1645+
}
1646+
15541647
#[doc(hidden)]
15551648
#[deprecated(note = "parallel `find` does not search in order -- use `find_any`, \\
15561649
`find_first`, or `find_last`")]

src/iter/plumbing/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ such as string characters.
140140
## What on earth is `ProducerCallback`?
141141

142142
We saw that when you call a parallel action method like
143-
`par_iter.reduce()`, that will creating a "reducing" consumer and then
143+
`par_iter.reduce()`, that will create a "reducing" consumer and then
144144
invoke `par_iter.drive_unindexed()` (or `par_iter.drive()`) as
145145
appropriate. This may create yet more consumers as we proceed up the
146146
parallel iterator chain. But at some point we're going to get to the

src/iter/test.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,50 @@ pub fn find_first_or_last() {
12711271
assert_eq!(a.par_iter().position_last(|&x| x < 0), None);
12721272
}
12731273

1274+
#[test]
1275+
pub fn find_map_first_or_last_or_any() {
1276+
let mut a: Vec<i32> = vec![];
1277+
1278+
assert!(a.par_iter().find_map_any(half_if_positive).is_none());
1279+
assert!(a.par_iter().find_map_first(half_if_positive).is_none());
1280+
assert!(a.par_iter().find_map_last(half_if_positive).is_none());
1281+
1282+
a = (-1024..-3).collect();
1283+
1284+
assert!(a.par_iter().find_map_any(half_if_positive).is_none());
1285+
assert!(a.par_iter().find_map_first(half_if_positive).is_none());
1286+
assert!(a.par_iter().find_map_last(half_if_positive).is_none());
1287+
1288+
assert!(a.par_iter().find_map_any(half_if_negative).is_some());
1289+
assert_eq!(
1290+
a.par_iter().find_map_first(half_if_negative),
1291+
Some(-512_i32)
1292+
);
1293+
assert_eq!(a.par_iter().find_map_last(half_if_negative), Some(-2_i32));
1294+
1295+
a.append(&mut (2..1025).collect());
1296+
1297+
assert!(a.par_iter().find_map_any(half_if_positive).is_some());
1298+
assert_eq!(a.par_iter().find_map_first(half_if_positive), Some(1_i32));
1299+
assert_eq!(a.par_iter().find_map_last(half_if_positive), Some(512_i32));
1300+
1301+
fn half_if_positive(x: &i32) -> Option<i32> {
1302+
if *x > 0 {
1303+
Some(x / 2)
1304+
} else {
1305+
None
1306+
}
1307+
}
1308+
1309+
fn half_if_negative(x: &i32) -> Option<i32> {
1310+
if *x < 0 {
1311+
Some(x / 2)
1312+
} else {
1313+
None
1314+
}
1315+
}
1316+
}
1317+
12741318
#[test]
12751319
pub fn check_find_not_present() {
12761320
let counter = AtomicUsize::new(0);

0 commit comments

Comments
 (0)