about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-09-14 17:10:51 -0700
committerbors <bors@rust-lang.org>2013-09-14 17:10:51 -0700
commit0dbd509e0f90bc13f7b49c24e48a281928b2ee9c (patch)
tree976d98009c9206efa782db271ccca2828bc37015 /src/libstd
parentcf7e93ff2571140abf299212ec9ebc0fcd9944eb (diff)
parenta18038f3b2ee338de05ef4489d6ac7067c9198fd (diff)
downloadrust-0dbd509e0f90bc13f7b49c24e48a281928b2ee9c.tar.gz
rust-0dbd509e0f90bc13f7b49c24e48a281928b2ee9c.zip
auto merge of #9199 : thestinger/rust/range_step, r=cmr
My focus was on getting these to be correct in all cases by handling overflow properly. I'll clean them up and work on the performance later.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/iter.rs115
1 files changed, 101 insertions, 14 deletions
diff --git a/src/libstd/iter.rs b/src/libstd/iter.rs
index ec3c02a31f2..22e8a1defbd 100644
--- a/src/libstd/iter.rs
+++ b/src/libstd/iter.rs
@@ -21,7 +21,7 @@ use cmp;
 use num::{Zero, One, Integer, CheckedAdd, CheckedSub, Saturating};
 use option::{Option, Some, None};
 use ops::{Add, Mul, Sub};
-use cmp::Ord;
+use cmp::{Eq, Ord};
 use clone::Clone;
 use uint;
 use util;
@@ -1719,7 +1719,21 @@ pub fn count<A>(start: A, step: A) -> Counter<A> {
     Counter{state: start, step: step}
 }
 
-/// A range of numbers from [0, N)
+impl<A: Add<A, A> + Clone> Iterator<A> for Counter<A> {
+    #[inline]
+    fn next(&mut self) -> Option<A> {
+        let result = self.state.clone();
+        self.state = self.state + self.step;
+        Some(result)
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (uint, Option<uint>) {
+        (uint::max_value, None) // Too bad we can't specify an infinite lower bound
+    }
+}
+
+/// An iterator over the range [start, stop)
 #[deriving(Clone, DeepClone)]
 pub struct Range<A> {
     priv state: A,
@@ -1749,14 +1763,12 @@ impl<A: Add<A, A> + Ord + Clone> Iterator<A> for Range<A> {
     // Blocked on #8605 Need numeric trait for converting to `Option<uint>`
 }
 
-impl<A: Sub<A, A> + Integer + Ord + Clone> DoubleEndedIterator<A> for Range<A> {
+/// `Integer` is required to ensure the range will be the same regardless of
+/// the direction it is consumed.
+impl<A: Integer + Ord + Clone> DoubleEndedIterator<A> for Range<A> {
     #[inline]
     fn next_back(&mut self) -> Option<A> {
         if self.stop > self.state {
-            // Integer doesn't technically define this rule, but we're going to assume that every
-            // Integer is reachable from every other one by adding or subtracting enough Ones. This
-            // seems like a reasonable-enough rule that every Integer should conform to, even if it
-            // can't be statically checked.
             self.stop = self.stop - self.one;
             Some(self.stop.clone())
         } else {
@@ -1765,7 +1777,7 @@ impl<A: Sub<A, A> + Integer + Ord + Clone> DoubleEndedIterator<A> for Range<A> {
     }
 }
 
-/// A range of numbers from [0, N]
+/// An iterator over the range [start, stop]
 #[deriving(Clone, DeepClone)]
 pub struct RangeInclusive<A> {
     priv range: Range<A>,
@@ -1826,17 +1838,78 @@ impl<A: Sub<A, A> + Integer + Ord + Clone> DoubleEndedIterator<A> for RangeInclu
     }
 }
 
-impl<A: Add<A, A> + Clone> Iterator<A> for Counter<A> {
+/// An iterator over the range [start, stop) by `step`. It handles overflow by stopping.
+#[deriving(Clone, DeepClone)]
+pub struct RangeStep<A> {
+    priv state: A,
+    priv stop: A,
+    priv step: A,
+    priv rev: bool
+}
+
+/// Return an iterator over the range [start, stop) by `step`. It handles overflow by stopping.
+#[inline]
+pub fn range_step<A: CheckedAdd + Ord + Clone + Zero>(start: A, stop: A, step: A) -> RangeStep<A> {
+    let rev = step < Zero::zero();
+    RangeStep{state: start, stop: stop, step: step, rev: rev}
+}
+
+impl<A: CheckedAdd + Ord + Clone> Iterator<A> for RangeStep<A> {
     #[inline]
     fn next(&mut self) -> Option<A> {
-        let result = self.state.clone();
-        self.state = self.state + self.step;
-        Some(result)
+        if (self.rev && self.state > self.stop) || self.state < self.stop {
+            let result = self.state.clone();
+            match self.state.checked_add(&self.step) {
+                Some(x) => self.state = x,
+                None => self.state = self.stop.clone()
+            }
+            Some(result)
+        } else {
+            None
+        }
     }
+}
+
+/// An iterator over the range [start, stop] by `step`. It handles overflow by stopping.
+#[deriving(Clone, DeepClone)]
+pub struct RangeStepInclusive<A> {
+    priv state: A,
+    priv stop: A,
+    priv step: A,
+    priv rev: bool,
+    priv done: bool
+}
 
+/// Return an iterator over the range [start, stop] by `step`. It handles overflow by stopping.
+#[inline]
+pub fn range_step_inclusive<A: CheckedAdd + Ord + Clone + Zero>(start: A, stop: A,
+                                                                step: A) -> RangeStepInclusive<A> {
+    let rev = step < Zero::zero();
+    RangeStepInclusive{state: start, stop: stop, step: step, rev: rev, done: false}
+}
+
+impl<A: CheckedAdd + Ord + Clone + Eq> Iterator<A> for RangeStepInclusive<A> {
     #[inline]
-    fn size_hint(&self) -> (uint, Option<uint>) {
-        (uint::max_value, None) // Too bad we can't specify an infinite lower bound
+    fn next(&mut self) -> Option<A> {
+        if !self.done {
+            if (self.rev && self.state > self.stop) || self.state < self.stop {
+                let result = self.state.clone();
+                match self.state.checked_add(&self.step) {
+                    Some(x) => self.state = x,
+                    None => self.done = true
+                }
+                Some(result)
+            } else {
+                if self.state == self.stop {
+                    self.done = true;
+                    Some(self.state.clone())
+                } else {
+                    None
+                }
+            }
+        } else {
+            None
+        }
     }
 }
 
@@ -2650,6 +2723,20 @@ mod tests {
     }
 
     #[test]
+    fn test_range_step() {
+        assert_eq!(range_step(0i, 20, 5).collect::<~[int]>(), ~[0, 5, 10, 15]);
+        assert_eq!(range_step(20i, 0, -5).collect::<~[int]>(), ~[20, 15, 10, 5]);
+        assert_eq!(range_step(200u8, 255, 50).collect::<~[u8]>(), ~[200u8, 250]);
+    }
+
+    #[test]
+    fn test_range_step_inclusive() {
+        assert_eq!(range_step_inclusive(0i, 20, 5).collect::<~[int]>(), ~[0, 5, 10, 15, 20]);
+        assert_eq!(range_step_inclusive(20i, 0, -5).collect::<~[int]>(), ~[20, 15, 10, 5, 0]);
+        assert_eq!(range_step_inclusive(200u8, 255, 50).collect::<~[u8]>(), ~[200u8, 250]);
+    }
+
+    #[test]
     fn test_reverse() {
         let mut ys = [1, 2, 3, 4, 5];
         ys.mut_iter().reverse_();