class Range[A: (Real[A] val & Number)] is Iterator[A]
"""
A bounded numeric sequence with a given step size. Two named constructors
control whether the far bound is included:
* `Range.till` — excludes the far bound
* `Range.through` — includes the far bound
Direction is inferred: when `from < to` the range ascends, when `from > to`
it descends. Step is always a positive magnitude.
A zero or negative step, or a non-finite float argument (NaN or infinity),
produces an empty range.
```pony
// 0, 1, 2, 3, 4
for i in Range[USize].till(0, 5) do
env.out.print(i.string())
end
// 5, 4, 3, 2, 1, 0
for i in Range[I32].through(5, 0) do
env.out.print(i.string())
end
// with a step size
for i in Range[USize].till(0, 10, 2) do
env.out.print(i.string()) // 0, 2, 4, 6, 8
end
```
"""
let _from: A
let _to: A
let _step: A
// true when the range ascends
let _forward: Bool
let _inclusive: Bool
// true when constructor args are invalid; rewind cannot clear this
let _empty: Bool
var _idx: A
// set when _idx wraps past the type boundary, preventing infinite loops
var _done: Bool = false
new through(from: A, to: A, step: A = 1) =>
"""
Inclusive of the far bound.
`Range.through(0, 3)` yields 0, 1, 2, 3.
`Range.through(3, 0)` yields 3, 2, 1, 0.
`Range.through(5, 5)` yields 5.
`Range.through(0, 9, 3)` yields 0, 3, 6, 9.
`Range.through(0, 10, 3)` yields 0, 3, 6, 9 — the step skips the far bound.
"""
_from = from
_to = to
_step = step
_forward = from <= to
_inclusive = true
_idx = from
_empty =
if step <= 0 then true
else
iftype A <: FloatingPoint[A] then
(not from.finite()) or (not to.finite()) or (not step.finite())
else
false
end
end
new till(from: A, to: A, step: A = 1) =>
"""
Exclusive of the far bound.
`Range.till(0, 3)` yields 0, 1, 2.
`Range.till(3, 0)` yields 3, 2, 1.
`Range.till(5, 5)` yields nothing.
"""
_from = from
_to = to
_step = step
_forward = from < to
_inclusive = false
_idx = from
_empty =
if step <= 0 then true
else
iftype A <: FloatingPoint[A] then
(not from.finite()) or (not to.finite()) or (not step.finite())
else
false
end
end
fun has_next(): Bool =>
"""
True while there are values remaining in the sequence.
"""
if _empty or _done then return false end
if _forward then
if _inclusive then _idx <= _to else _idx < _to end
else
if _inclusive then _idx >= _to else _idx > _to end
end
fun ref next(): A ? =>
"""
Return the current value and advance. Errors when exhausted.
"""
if has_next() then
let current = _idx
if _forward then
_idx = current + _step
if _idx < current then _done = true end
else
_idx = current - _step
if _idx > current then _done = true end
end
current
else
error
end
fun ref rewind() =>
"""
Reset the iterator to its starting position.
"""
_idx = _from
_done = false