std.range
Range (Range) iteration and adapters.
use std.rangeRange literals
| Syntax | Meaning |
|---|---|
a..b | From a to b, step 1 |
a..b..s | From a to b, step s |
A range is exclusive of the end value (half-open, left-closed right-open). The step can be negative to indicate a decreasing range.
Iterator protocol
iter returns a Result—when the step is 0 it is the error path (E6009), so you must unwrap or handle it explicitly:
use std.assert
use std.list
use std.range
use std.result
main: () -> Void = {
it = result.unwrap(range.iter(1..4))
assert(range.has_next(it))
}Move semantics: The signatures of
has_nextandnextdo not take&, so they move the iterator. Therefore, you must create a new iterator on every access, or just iterate withfor ... in. This matches the iterator instd.list.
use std.assert
use std.range
use std.result
main: () -> Void = {
// create a new iterator each time
a = result.unwrap(range.iter(1..3))
assert(range.has_next(a))
b = result.unwrap(range.iter(1..3))
assert(range.next(b) == 1)
}For everyday iteration, just use for ... in directly:
use std.assert
use std.list
main: () -> Void = {
nums = [1, 2, 3]
mut sum = 0
for x in nums {
sum = sum + x
}
assert(sum == 6)
}Function overview
| Function | Signature |
|---|---|
iter | (r: Range(Int)) -> Result(Iterator(Any), Error) |
has_next | (it: Iterator(Any)) -> Bool |
next | (it: &Iterator(Any)) -> Any |
contains | (r: Range(Int), x: Int) -> Result(Bool, Error) |
abort_invalid_step | (r: Range(Int)) -> Any |
map | (it: Iterator(Any), f: (Any) -> Any) -> Iterator(Any) |
filter | (it: Iterator(Any), p: (Any) -> Bool) -> Iterator(Any) |
collect | (it: Iterator(Any)) -> Vec(Any) |
reduce | (it: Iterator(Any), init: Any, f: (Any, Any) -> Any) -> Any |
for_each | (it: Iterator(Any), f: (Any) -> Void) -> Void |
Iterator protocol
iter
iter: (r: Range(Int)) -> Result(Iterator(Any), Error)Creates an iterator from a range.
r— the range, e.g.1..6or3..0..-1
Returns: Result.ok(iterator) on success; Result.err with code E6009 when the step is 0.
use std.assert
use std.range
use std.result
main: () -> Void = {
it = result.unwrap(range.iter(1..4))
assert(range.has_next(it))
}has_next
has_next: (it: Iterator(Any)) -> BoolWhether there are still unconsumed elements.
Moves the iterator.
use std.assert
use std.range
use std.result
main: () -> Void = {
it = result.unwrap(range.iter(1..4))
assert(range.has_next(it))
}next
next: (it: &Iterator(Any)) -> AnyTakes the current element and advances the internal cursor by one position.
Returns: the current element; Void when iteration ends.
Moves the iterator.
use std.assert
use std.range
use std.result
main: () -> Void = {
it = result.unwrap(range.iter(1..4))
assert(range.next(it) == 1)
}Decreasing ranges are supported as well:
use std.assert
use std.range
use std.result
main: () -> Void = {
desc = result.unwrap(range.iter(3..0..-1))
assert(range.next(desc) == 3)
}contains
contains: (r: Range(Int), x: Int) -> Result(Bool, Error)Checks whether x falls within the range.
r— the rangex— the value to test
Returns: Result.ok(Bool). The end value is excluded (open); when a step is given, only elements aligned with the step are matched.
use std.assert
use std.range
use std.result
main: () -> Void = {
assert(result.unwrap(range.contains(1..10, 5)))
assert(!result.unwrap(range.contains(1..10, 10))) // end value excluded
assert(result.unwrap(range.contains(0..10..2, 4))) // aligned with step
assert(!result.unwrap(range.contains(0..10..2, 3))) // not aligned
}abort_invalid_step
abort_invalid_step: (r: Range(Int)) -> AnyThe abort hook for an invalid step, invoked when for ... in consumes a range whose step is 0.
Always raises E6007 with the message Range step must be non-zero (for/in consumption). Normal code does not need to call this directly.
use std.range
main: () -> Void = {
// using iter directly gives you an Err, so you do not need this hook
r = range.iter(1..3)
}Adapters
map and filter return lazy adapters—they do not compute immediately, and only produce results once consumed by collect / reduce / for_each / for ... in.
map
map: (it: Iterator(Any), f: (Any) -> Any) -> Iterator(Any)Maps f over every element and returns a new lazy iterator.
use std.assert
use std.list
use std.range
use std.result
main: () -> Void = {
doubled = range.collect(range.map(result.unwrap(range.iter(1..4)), x => x * 2))
assert(list.len(doubled) == 3)
assert(doubled[0] == 2)
}filter
filter: (it: Iterator(Any), p: (Any) -> Bool) -> Iterator(Any)Keeps the elements for which p is true, and returns a new lazy iterator.
use std.assert
use std.list
use std.range
use std.result
main: () -> Void = {
big = range.collect(range.filter(result.unwrap(range.iter(1..6)), x => x > 3))
assert(list.len(big) == 2)
assert(big[0] == 4)
}Adapters can be chained:
use std.assert
use std.list
use std.range
use std.result
main: () -> Void = {
r = 1..6
chained = range.collect(range.map(range.filter(result.unwrap(range.iter(r)), x => x % 2 == 0), x => x * 10))
// value semantics: `chained` is consumed by indexed reads, so bind each value to a local
first = chained[0]
second = chained[1]
assert(first == 20)
assert(second == 40)
}collect
collect: (it: Iterator(Any)) -> Vec(Any)Consumes the iterator and collects all elements into a List.
use std.assert
use std.list
use std.range
use std.result
main: () -> Void = {
xs = range.collect(result.unwrap(range.iter(1..4)))
assert(list.len(xs) == 3)
}reduce
reduce: (it: Iterator(Any), init: Any, f: (Any, Any) -> Any) -> AnyConsumes the iterator and folds it.
it— the iteratorinit— the initial accumulator valuef— the reduction function(accumulator, element) -> new accumulator
Note that the argument order differs from
std.list.reduce: in this module it is(iterator, init, function), whilestd.listis(list, function, init).
use std.assert
use std.range
use std.result
main: () -> Void = {
total = range.reduce(result.unwrap(range.iter(1..6)), 0, (acc, x) => acc + x)
assert(total == 15)
}for_each
for_each: (it: Iterator(Any), f: (Any) -> Void) -> VoidRuns f on every element, intended for side effects.
use std.assert
use std.range
use std.result
main: () -> Void = {
// prints 1, 2, 3
range.for_each(result.unwrap(range.iter(1..4)), x => println(x))
assert(true)
}Closures currently cannot capture and mutate an outer
mutvariable, so accumulating withfor_eachis not possible (it raisesE1001)—usereducefor accumulation.
Related
std.list— lists and their iteratorsstd.result— unwrapping the return value ofiter/contains- Error code reference —
E6009invalid step
