From c767441fa889b7bfc5ae96b4f3be980d69aa1778 Mon Sep 17 00:00:00 2001 From: Linus Behrbohm Date: Sat, 10 Apr 2021 16:07:01 +0200 Subject: [PATCH 1/7] Add find_or_last method --- src/lib.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 22649148f..65bf62689 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![warn(missing_docs)] #![crate_name="itertools"] #![cfg_attr(not(feature = "use_std"), no_std)] +#![feature(control_flow_enum)] //! Extra iterator adaptors, functions and macros. //! @@ -75,6 +76,7 @@ use std::fmt::Write; type VecIntoIter = alloc::vec::IntoIter; #[cfg(feature = "use_alloc")] use std::iter::FromIterator; +use std::ops::ControlFlow; #[macro_use] mod impl_macros; @@ -1730,7 +1732,32 @@ pub trait Itertools : Iterator { } None } + /// Find the value of the first element satisfying a predicate or return the last element, if any. + /// + /// The iterator is not advanced past the first element found. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let numbers = [1, 2, 3, 4]; + /// assert_eq!(numbers.iter().find_or_last(|x| x > 5), Some(4)); + /// ``` + fn find_or_last

(mut self, predicate: P) -> Option + where Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + #[inline] + fn check(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut(Option, T) -> ControlFlow> { + move |_, x| { + if predicate(&x) { ControlFlow::Break(x) } else { ControlFlow::Continue(Some(x)) } + } + } + match self.try_fold(None, check(predicate)) { + ControlFlow::Continue(x) => x, + ControlFlow::Break(x) => Some(x), + } + } /// Returns `true` if the given item is present in this iterator. /// /// This method is short-circuiting. If the given item is present in this From b773be3d3da6ad55d1c8f93b87fa62cc20d7f981 Mon Sep 17 00:00:00 2001 From: Linus Behrbohm Date: Sat, 10 Apr 2021 16:15:03 +0200 Subject: [PATCH 2/7] Use Result instead of ControlFlow --- src/lib.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 65bf62689..1a7b979b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![warn(missing_docs)] #![crate_name="itertools"] #![cfg_attr(not(feature = "use_std"), no_std)] -#![feature(control_flow_enum)] //! Extra iterator adaptors, functions and macros. //! @@ -76,7 +75,6 @@ use std::fmt::Write; type VecIntoIter = alloc::vec::IntoIter; #[cfg(feature = "use_alloc")] use std::iter::FromIterator; -use std::ops::ControlFlow; #[macro_use] mod impl_macros; @@ -1747,16 +1745,13 @@ pub trait Itertools : Iterator { P: FnMut(&Self::Item) -> bool, { #[inline] - fn check(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut(Option, T) -> ControlFlow> { + fn check(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut(Option, T) -> Result, T> { move |_, x| { - if predicate(&x) { ControlFlow::Break(x) } else { ControlFlow::Continue(Some(x)) } + if predicate(&x) { Result::Err(x) } else { Result::Ok(Some(x)) } } } - match self.try_fold(None, check(predicate)) { - ControlFlow::Continue(x) => x, - ControlFlow::Break(x) => Some(x), - } + self.try_fold(None, check(predicate)).unwrap_or_else(Some) } /// Returns `true` if the given item is present in this iterator. /// From 7bd13fc37df69313bc6e605535c02b2c8bf55c69 Mon Sep 17 00:00:00 2001 From: Linus Behrbohm Date: Sat, 10 Apr 2021 16:25:45 +0200 Subject: [PATCH 3/7] Fix doc test --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1a7b979b1..a8537f653 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1738,7 +1738,7 @@ pub trait Itertools : Iterator { /// use itertools::Itertools; /// /// let numbers = [1, 2, 3, 4]; - /// assert_eq!(numbers.iter().find_or_last(|x| x > 5), Some(4)); + /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 5), Some(&4)); /// ``` fn find_or_last

(mut self, predicate: P) -> Option where Self: Sized, From b5c124213406a9e449968ac39a315d2bc6df59d4 Mon Sep 17 00:00:00 2001 From: Linus Behrbohm Date: Sat, 10 Apr 2021 16:29:25 +0200 Subject: [PATCH 4/7] Add more tests to doc test --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index a8537f653..30a8dcd75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1739,6 +1739,8 @@ pub trait Itertools : Iterator { /// /// let numbers = [1, 2, 3, 4]; /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 5), Some(&4)); + /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 2), Some(&3)); + /// assert_eq!(std::iter::empty::().find_or_last(|&x| x > 5), None); /// ``` fn find_or_last

(mut self, predicate: P) -> Option where Self: Sized, From f02cfd93931f7fa40cb751722c1ed608b68723e8 Mon Sep 17 00:00:00 2001 From: Linus Behrbohm Date: Sat, 10 Apr 2021 20:40:06 +0200 Subject: [PATCH 5/7] Fix indentation --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 30a8dcd75..bd241ee1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1742,11 +1742,11 @@ pub trait Itertools : Iterator { /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 2), Some(&3)); /// assert_eq!(std::iter::empty::().find_or_last(|&x| x > 5), None); /// ``` - fn find_or_last

(mut self, predicate: P) -> Option - where Self: Sized, - P: FnMut(&Self::Item) -> bool, - { - #[inline] + fn find_or_last

(mut self, predicate: P) -> Option + where Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + #[inline] fn check(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut(Option, T) -> Result, T> { move |_, x| { if predicate(&x) { Result::Err(x) } else { Result::Ok(Some(x)) } @@ -1754,7 +1754,7 @@ pub trait Itertools : Iterator { } self.try_fold(None, check(predicate)).unwrap_or_else(Some) - } + } /// Returns `true` if the given item is present in this iterator. /// /// This method is short-circuiting. If the given item is present in this From 4a52f3cca7ab5bdf60eb5c24cc4aebe506a0cf74 Mon Sep 17 00:00:00 2001 From: Linus Behrbohm Date: Sat, 10 Apr 2021 21:16:37 +0200 Subject: [PATCH 6/7] Implement alternative implementation of find_or_last using accumulator variable and find_map --- src/lib.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bd241ee1f..bc3eb0f12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1742,18 +1742,13 @@ pub trait Itertools : Iterator { /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 2), Some(&3)); /// assert_eq!(std::iter::empty::().find_or_last(|&x| x > 5), None); /// ``` - fn find_or_last

(mut self, predicate: P) -> Option + fn find_or_last

(mut self, mut predicate: P) -> Option where Self: Sized, P: FnMut(&Self::Item) -> bool, { - #[inline] - fn check(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut(Option, T) -> Result, T> { - move |_, x| { - if predicate(&x) { Result::Err(x) } else { Result::Ok(Some(x)) } - } - } - - self.try_fold(None, check(predicate)).unwrap_or_else(Some) + let mut prev = None; + self.find_map(|x| if predicate(&x) { Some(x) } else { prev = Some(x); None }) + .or(prev) } /// Returns `true` if the given item is present in this iterator. /// From 086b7d2f29ec325104afa9a26f7de37f3551a7d1 Mon Sep 17 00:00:00 2001 From: Linus Behrbohm Date: Sat, 10 Apr 2021 21:26:40 +0200 Subject: [PATCH 7/7] Add find_or_first method to Itertools trait --- src/lib.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index bc3eb0f12..bc2d8c2f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1750,6 +1750,29 @@ pub trait Itertools : Iterator { self.find_map(|x| if predicate(&x) { Some(x) } else { prev = Some(x); None }) .or(prev) } + /// Find the value of the first element satisfying a predicate or return the first element, if any. + /// + /// The iterator is not advanced past the first element found. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let numbers = [1, 2, 3, 4]; + /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 5), Some(&1)); + /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 2), Some(&3)); + /// assert_eq!(std::iter::empty::().find_or_first(|&x| x > 5), None); + /// ``` + fn find_or_first

(mut self, mut predicate: P) -> Option + where Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + let first = self.next()?; + Some(if predicate(&first) { + first + } else { + self.find(|x| predicate(&x)).unwrap_or(first) + }) + } /// Returns `true` if the given item is present in this iterator. /// /// This method is short-circuiting. If the given item is present in this