diff --git a/src/libstd/iter.rs b/src/libstd/iter.rs index df7b04dcd19..69260e4575d 100644 --- a/src/libstd/iter.rs +++ b/src/libstd/iter.rs @@ -882,6 +882,39 @@ pub trait OrdIterator { /// assert!(a.iter().min().unwrap() == &1); /// ``` fn min(&mut self) -> Option; + + /// `min_max` finds the mininum and maximum elements in the iterator. + /// + /// The return type `MinMaxResult` is an enum of three variants: + /// - `NoElements` if the iterator is empty. + /// - `OneElement(x)` if the iterator has exactly one element. + /// - `MinMax(x, y)` is returned otherwise, where `x <= y`. Two values are equal if and only if + /// there is more than one element in the iterator and all elements are equal. + /// + /// On an iterator of length `n`, `min_max` does `1.5 * n` comparisons, + /// and so faster than calling `min` and `max separately which does `2 * n` comparisons. + /// + /// # Example + /// + /// ```rust + /// use std::iter::{NoElements, OneElement, MinMax}; + /// + /// let v: [int, ..0] = []; + /// assert_eq!(v.iter().min_max(), NoElements); + /// + /// let v = [1i]; + /// assert!(v.iter().min_max() == OneElement(&1)); + /// + /// let v = [1i, 2, 3, 4, 5]; + /// assert!(v.iter().min_max() == MinMax(&1, &5)); + /// + /// let v = [1i, 2, 3, 4, 5, 6]; + /// assert!(v.iter().min_max() == MinMax(&1, &6)); + /// + /// let v = [1i, 1, 1, 1]; + /// assert!(v.iter().min_max() == MinMax(&1, &1)); + /// ``` + fn min_max(&mut self) -> MinMaxResult; } impl> OrdIterator for T { @@ -904,6 +937,91 @@ impl> OrdIterator for T { } }) } + + fn min_max(&mut self) -> MinMaxResult { + let (mut min, mut max) = match self.next() { + None => return NoElements, + Some(x) => { + match self.next() { + None => return OneElement(x), + Some(y) => if x < y {(x, y)} else {(y,x)} + } + } + }; + + loop { + // `first` and `second` are the two next elements we want to look at. + // We first compare `first` and `second` (#1). The smaller one is then compared to + // current mininum (#2). The larger one is compared to current maximum (#3). This + // way we do 3 comparisons for 2 elements. + let first = match self.next() { + None => break, + Some(x) => x + }; + let second = match self.next() { + None => { + if first < min { + min = first; + } else if first > max { + max = first; + } + break; + } + Some(x) => x + }; + if first < second { + if first < min {min = first;} + if max < second {max = second;} + } else { + if second < min {min = second;} + if max < first {max = first;} + } + } + + MinMax(min, max) + } +} + +/// `MinMaxResult` is an enum returned by `min_max`. See `OrdIterator::min_max` for more detail. +#[deriving(Clone, Eq)] +pub enum MinMaxResult { + /// Empty iterator + NoElements, + + /// Iterator with one element, so the minimum and maximum are the same + OneElement(T), + + /// More than one element in the iterator, the first element is not larger than the second + MinMax(T, T) +} + +impl MinMaxResult { + /// `into_option` creates an `Option` of type `(T,T)`. The returned `Option` has variant + /// `None` if and only if the `MinMaxResult` has variant `NoElements`. Otherwise variant + /// `Some(x,y)` is returned where `x <= y`. If `MinMaxResult` has variant `OneElement(x)`, + /// performing this operation will make one clone of `x`. + /// + /// # Example + /// + /// ```rust + /// use std::iter::{NoElements, OneElement, MinMax, MinMaxResult}; + /// + /// let r: MinMaxResult = NoElements; + /// assert_eq!(r.into_option(), None) + /// + /// let r = OneElement(1); + /// assert_eq!(r.into_option(), Some((1,1))); + /// + /// let r = MinMax(1,2); + /// assert_eq!(r.into_option(), Some((1,2))); + /// ``` + pub fn into_option(self) -> Option<(T,T)> { + match self { + NoElements => None, + OneElement(x) => Some((x.clone(), x)), + MinMax(x, y) => Some((x, y)) + } + } } /// A trait for iterators that are cloneable. @@ -2944,4 +3062,34 @@ mod tests { it.next(); assert!( it.is_empty() ); } + + #[test] + fn test_min_max() { + let v: [int, ..0] = []; + assert_eq!(v.iter().min_max(), NoElements); + + let v = [1i]; + assert!(v.iter().min_max() == OneElement(&1)); + + let v = [1i, 2, 3, 4, 5]; + assert!(v.iter().min_max() == MinMax(&1, &5)); + + let v = [1i, 2, 3, 4, 5, 6]; + assert!(v.iter().min_max() == MinMax(&1, &6)); + + let v = [1i, 1, 1, 1]; + assert!(v.iter().min_max() == MinMax(&1, &1)); + } + + #[test] + fn test_MinMaxResult() { + let r: MinMaxResult = NoElements; + assert_eq!(r.into_option(), None) + + let r = OneElement(1); + assert_eq!(r.into_option(), Some((1,1))); + + let r = MinMax(1,2); + assert_eq!(r.into_option(), Some((1,2))); + } }