diff --git a/src/iter/mod.rs b/src/iter/mod.rs index 35f3f0e94..fce24e884 100644 --- a/src/iter/mod.rs +++ b/src/iter/mod.rs @@ -1504,7 +1504,7 @@ pub trait ParallelIterator: Sized + Send { /// just want the first match that discovered anywhere in the iterator, /// `find_any` is a better choice. /// - /// # Exmaples + /// # Examples /// /// ``` /// use rayon::prelude::*; @@ -1551,6 +1551,99 @@ pub trait ParallelIterator: Sized + Send { find_first_last::find_last(self, predicate) } + /// Applies the given predicate to the items in the parallel iterator + /// and returns **any** non-None result of the map operation. + /// + /// Once a non-None value is produced from the map operation, we will + /// attempt to stop processing the rest of the items in the iterator + /// as soon as possible. + /// + /// Note that this method only returns **some** item in the parallel + /// iterator that is not None from the map predicate. The item returned + /// may not be the **first** non-None value produced in the parallel + /// sequence, since the entire sequence is mapped over in parallel. + /// + /// # Examples + /// + /// ``` + /// use rayon::prelude::*; + /// + /// let c = ["lol", "NaN", "5", "5"]; + /// + /// let first_number = c.par_iter().find_map_first(|s| s.parse().ok()); + /// + /// assert_eq!(first_number, Some(5)); + /// ``` + fn find_map_any(self, predicate: P) -> Option + where + P: Fn(Self::Item) -> Option + Sync + Send, + R: Send, + { + self.filter_map(predicate).find_any(|_| true) + } + + /// Applies the given predicate to the items in the parallel iterator and + /// returns the sequentially **first** non-None result of the map operation. + /// + /// Once a non-None value is produced from the map operation, all attempts + /// to the right of the match will be stopped, while attempts to the left + /// must continue in case an earlier match is found. + /// + /// Note that not all parallel iterators have a useful order, much like + /// sequential `HashMap` iteration, so "first" may be nebulous. If you + /// just want the first non-None value discovered anywhere in the iterator, + /// `find_map_any` is a better choice. + /// + /// # Examples + /// + /// ``` + /// use rayon::prelude::*; + /// + /// let c = ["lol", "NaN", "2", "5"]; + /// + /// let first_number = c.par_iter().find_map_first(|s| s.parse().ok()); + /// + /// assert_eq!(first_number, Some(2)); + /// ``` + fn find_map_first(self, predicate: P) -> Option + where + P: Fn(Self::Item) -> Option + Sync + Send, + R: Send, + { + self.filter_map(predicate).find_first(|_| true) + } + + /// Applies the given predicate to the items in the parallel iterator and + /// returns the sequentially **last** non-None result of the map operation. + /// + /// Once a non-None value is produced from the map operation, all attempts + /// to the left of the match will be stopped, while attempts to the right + /// must continue in case a later match is found. + /// + /// Note that not all parallel iterators have a useful order, much like + /// sequential `HashMap` iteration, so "first" may be nebulous. If you + /// just want the first non-None value discovered anywhere in the iterator, + /// `find_map_any` is a better choice. + /// + /// # Examples + /// + /// ``` + /// use rayon::prelude::*; + /// + /// let c = ["lol", "NaN", "2", "5"]; + /// + /// let first_number = c.par_iter().find_map_last(|s| s.parse().ok()); + /// + /// assert_eq!(first_number, Some(5)); + /// ``` + fn find_map_last(self, predicate: P) -> Option + where + P: Fn(Self::Item) -> Option + Sync + Send, + R: Send, + { + self.filter_map(predicate).find_last(|_| true) + } + #[doc(hidden)] #[deprecated(note = "parallel `find` does not search in order -- use `find_any`, \\ `find_first`, or `find_last`")] diff --git a/src/iter/plumbing/README.md b/src/iter/plumbing/README.md index 8677369ae..cd94eae30 100644 --- a/src/iter/plumbing/README.md +++ b/src/iter/plumbing/README.md @@ -140,7 +140,7 @@ such as string characters. ## What on earth is `ProducerCallback`? We saw that when you call a parallel action method like -`par_iter.reduce()`, that will creating a "reducing" consumer and then +`par_iter.reduce()`, that will create a "reducing" consumer and then invoke `par_iter.drive_unindexed()` (or `par_iter.drive()`) as appropriate. This may create yet more consumers as we proceed up the parallel iterator chain. But at some point we're going to get to the diff --git a/src/iter/test.rs b/src/iter/test.rs index 069436a7b..900de5add 100644 --- a/src/iter/test.rs +++ b/src/iter/test.rs @@ -1271,6 +1271,50 @@ pub fn find_first_or_last() { assert_eq!(a.par_iter().position_last(|&x| x < 0), None); } +#[test] +pub fn find_map_first_or_last_or_any() { + let mut a: Vec = vec![]; + + assert!(a.par_iter().find_map_any(half_if_positive).is_none()); + assert!(a.par_iter().find_map_first(half_if_positive).is_none()); + assert!(a.par_iter().find_map_last(half_if_positive).is_none()); + + a = (-1024..-3).collect(); + + assert!(a.par_iter().find_map_any(half_if_positive).is_none()); + assert!(a.par_iter().find_map_first(half_if_positive).is_none()); + assert!(a.par_iter().find_map_last(half_if_positive).is_none()); + + assert!(a.par_iter().find_map_any(half_if_negative).is_some()); + assert_eq!( + a.par_iter().find_map_first(half_if_negative), + Some(-512_i32) + ); + assert_eq!(a.par_iter().find_map_last(half_if_negative), Some(-2_i32)); + + a.append(&mut (2..1025).collect()); + + assert!(a.par_iter().find_map_any(half_if_positive).is_some()); + assert_eq!(a.par_iter().find_map_first(half_if_positive), Some(1_i32)); + assert_eq!(a.par_iter().find_map_last(half_if_positive), Some(512_i32)); + + fn half_if_positive(x: &i32) -> Option { + if *x > 0 { + Some(x / 2) + } else { + None + } + } + + fn half_if_negative(x: &i32) -> Option { + if *x < 0 { + Some(x / 2) + } else { + None + } + } +} + #[test] pub fn check_find_not_present() { let counter = AtomicUsize::new(0);