How does in drive for-loop iteration over ranges, and how do you iterate descending or with a step?
answer
- for-in uses iterator(), membership-in uses contains
- .. is ascending only; 5..1 is empty
- downTo to descend; step to skip
- downTo/step return IntProgression (step can be negative)
- Range for-loops compile to counter loops, no Iterator alloc
basics
~10 sfor (i in 1..5) loops 1 to 5 because the range can be iterated. To go down use downTo (5 downTo 1), and to skip values use step (1..10 step 2).
solid answer
~50 sIn a `for` header, `for (x in range)` uses the `iterator()` operator convention, not `contains`. `IntRange` implements `Iterable<Int>`, so it produces an iterator that walks each value ascending. A plain `..` range **cannot count down**: `for (i in 5..1)` is empty. To descend use the `downTo` infix function: `for (i in 5 downTo 1)`. To skip values use the `step` infix function: `for (i in 1..10 step 2)` yields 1,3,5,7,9. Both `downTo` and `step` return an **`IntProgression`** (a generalization of `IntRange` with an explicit step that can be negative). Progressions still support `in`, `first`, `last`, and `reversed()`. The compiler also performs a key optimization: iterating over a literal range or progression compiles to a **plain counter-based loop with no `Iterator` object allocated**, so `for (i in 0..<n)` is as cheap as a Java index loop.
code
kotlin · 6 linesfor (i in 1..5) print(i) // 12345
for (i in 5 downTo 1) print(i) // 54321
for (i in 1..10 step 2) print("$i ") // 1 3 5 7 9
val p = 1..10 step 3
println(p.last) // 10 (clamped to grid)
println(9 in p) // false (not on step grid)go deeper
Knows for (i in 1..5) loops and downTo counts down.
Uses step and downTo correctly and knows they yield progressions.
Distinguishes iterator-in from contains-in, explains progression last clamping and step-grid membership.
Reasons about the compiler's loop-lowering / zero-allocation guarantee and the Range-vs-Progression type design tradeoffs.
## Iteration uses iterator(), not contains When `in` appears in a `for` header, Kotlin requires the right operand to provide an `operator fun iterator()`. `IntRange` (via `Iterable<Int>`) does, so: ```kotlin for (i in 1..5) print(i) // 12345 ``` walks every value ascending. This is a **different convention** from membership: membership-`in` needs `contains`, iteration-`in` needs `iterator()`. ## Ascending only by default A `..` (or `..<`) range is always ascending. If the start exceeds the end it is **empty** — it does not count down: ```kotlin for (i in 5..1) print(i) // prints nothing (empty range) ``` ## downTo for descending Use the `downTo` infix function: ```kotlin for (i in 5 downTo 1) print(i) // 54321 ``` `5 downTo 1` returns an **`IntProgression`** with step -1. ## step to skip Use the `step` infix function (step must be a positive Int; direction is set by `..`/`downTo`): ```kotlin for (i in 1..10 step 2) print("$i ") // 1 3 5 7 9 for (i in 10 downTo 1 step 3) print("$i ") // 10 7 4 1 ``` Both produce an `IntProgression`. The `last` value is computed so the progression never overshoots the bound: `1..10 step 3` has `last == 10`, but `1..9 step 3` has `last == 7`. ## Range vs Progression - `IntRange` = a progression with step 1, also a `ClosedRange` (so it has a meaningful `contains`). - `IntProgression` = `first`, `last`, `step` (possibly negative). `downTo`/`step` yield this. It is `Iterable` and supports `in`, but its `last` is the last reachable element, not necessarily the nominal bound. ```kotlin val p = 1..10 step 3 println(p.last) // 10 println(10 in p) // true (10 is reachable) println(9 in p) // false (not on the step grid) ``` ## The allocation optimization The Kotlin compiler **special-cases** `for` loops over ranges and progressions: it lowers them to a counter-based loop using `first`, `last`, and `step` with **no `Iterator` instance allocated** on the heap. So `for (i in 0..<n)` has the same cost as a hand-written index loop — there is no per-iteration boxing or object churn for the standard primitive ranges. ## reversed() `(1..5).reversed()` returns an `IntProgression` 5..1 step -1, an alternative to `downTo`. ## Key APIs/keywords - `operator fun iterator()` (iteration convention) - `downTo`, `step`, `reversed()` (infix/functions) - `IntRange`, `IntProgression`, `.first`, `.last`, `.step` - compiler loop-lowering: no Iterator allocation for primitive ranges
- Why does for (i in 5..1) print nothing?`..` always builds an ascending range; when start > end it is empty and iterates zero times. Use `5 downTo 1` to count down.
- Does iterating a range allocate an Iterator?No. The compiler lowers for-loops over primitive ranges/progressions to a counter-based loop, so no Iterator object is allocated.
- Is 9 in (1..10 step 3) true?No. The progression's reachable elements are 1,4,7,10; 9 is not on the step grid, so contains returns false even though 9 is between the bounds.
A range is a staircase you climb one step at a time; downTo turns you around, and step lets you take the stairs two at a time.
saying these in an interview costs you the question
- Expecting 5..1 to count down
- Thinking step can be negative (it must be positive; downTo sets direction)
- Assuming range iteration allocates an Iterator per loop
- Believing x in progression just checks the bounds (it checks the step grid too)
- Confusing IntRange and IntProgression semantics for last