A range loop counts through numbers.
to stops before the end value:
loop 0 to 5 |number|:
io.line([: [number]])
;
This produces 0, 1, 2, 3 and 4.
Use to & when the end value should be included:
loop 0 to & 5 |number|:
io.line([: [number]])
;
This also produces 5.
Start from zero
You can leave out the first 0.
loop to 5 |number|:
io.line([: [number]])
;
Change the step
Use by to skip between values:
loop 0 to 10 by 2 |number|:
io.line([: [number]])
;
This produces 0, 2, 4, 6 and 8.
Count downward
The direction comes from the bounds. You do not need a reverse keyword.
loop 5 to 0 |number|:
io.line([: [number]])
;
Float ranges need an explicit step:
loop 0.0 to 1.0 by 0.1 |amount|:
io.line([: [amount]])
;
A range loop generates numeric values directly from its loop header.
loop 0 to 10 by 2 |value, index|:
io.line([: [index]: [value]])
;
Forms
loop start to end:
...
loop start to & end |value|:
...
loop start to end by step |value, index|:
...
loop to end |value|:
...
loop to & end |value|:
...
Bounds
to uses an exclusive end bound.to & uses an inclusive end bound.loop to end starts at 0.loop to & end starts at 0 and includes the end value.- Ascending bounds count upward.
- Descending bounds count downward.
- There is no
reverse keyword. - When start and end are equal, an exclusive range has no iterations and an inclusive range has one.
Step
- Without
by, integer ranges use a step magnitude of 1. by supplies a magnitude. The compiler applies the direction implied by the bounds.- A literal zero step is rejected during compilation.
- A non-literal step is checked before the first iteration. A value of zero fails at runtime.
- Any range using
Float requires an explicit by step. - Start, end and step expressions must have numeric types.
loop 0.0 to 1.0 by 0.1 |amount|:
io.line([: [amount]])
;
Evaluation
Start, end and explicit step expressions are evaluated once, from left to right, before the first bounds check.
The loop keeps those evaluated values for the complete iteration.
Bindings
Bindings are optional.
The first binding receives the current range value. It has type Float when any range operand is Float. Otherwise it has type Int.
The optional second binding receives the zero-based iteration index and always has type Int.
Bindings use the same one-or-two-name |...| rules as collection loops.
Checked updates
Runtime range counter updates use checked numeric operations. Their failures follow the numeric failure mode of the enclosing function or entry-selected start code.
Const range loops diagnose statically known counter failures while folding.