Explain the range-building expressions in Kotlin: 1..10, until, downTo, and step. What does each produce and what are the gotchas (e.g. step with downTo, empty ranges)?
answer
- .. inclusive, until exclusive
- downTo descends, step is positive magnitude
- 5..1 is EMPTY not descending
- step -1 throws IllegalArgumentException
- stepped .last may differ from bound
basics
~10 s1..10 counts 1 to 10 including both ends. until stops one before the end. downTo counts backwards. step changes how big each jump is. Mixing them wrong can make an empty range.
solid answer
~50 sRange expressions are real objects, not loop syntax. `1..10` calls `rangeTo` and is **inclusive** (1,2,...,10). `1 until 10` is half-open, excluding the upper bound (1..9) — ideal for index loops. `10 downTo 1` is descending and inclusive. `step k` (k > 0) sets the stride; it works with `..`, `until`, and `downTo` (the step is always a positive magnitude even when descending). These produce `IntRange`/`IntProgression` (also Long, Char). Gotchas: an ascending range where start > end (e.g. `5..1`) is **empty**, not descending — you must use `downTo`. `step` must be positive or it throws `IllegalArgumentException`. The last value of a stepped range may not equal the bound (`1..10 step 3` ends at 10? no — 1,4,7,10, here it does; `1..9 step 3` -> 1,4,7). Use `.first`, `.last`, `.step`, `.isEmpty()`, and the `in` operator for membership checks.
code
kotlin · 5 linesprintln((1..9 step 3).toList()) // [1, 4, 7]
println((10 downTo 1 step 3).toList()) // [10, 7, 4, 1]
println((5..1).isEmpty()) // true (empty, not descending)
println((1..9 step 3).last) // 7, not 9
// (1..10 step -1) -> throws IllegalArgumentException at runtimego deeper
Knows .. is inclusive and until is exclusive and can write a basic counting loop.
Correctly combines downTo with step and knows ascending start>end ranges are empty.
Explains ranges are first-class IntRange/IntProgression objects, the positive-step rule, and that stepped .last may differ from the bound.
Discusses the Comparable/ClosedRange/Progression abstractions and why floating-point ranges lack iteration.
## Ranges are objects A range expression evaluates to a value you can store, pass around, and query — not just loop sugar. `val r = 1..10` has type `IntRange`. Stepped ones become `IntProgression`. ## The four builders - **`1..10`** — `rangeTo` operator. **Inclusive** of both ends: 1,2,...,10. `in` checks membership: `5 in 1..10` is true. - **`1 until 10`** — infix function, **half-open**: excludes the upper bound, yielding 1..9. The natural fit for `0 until size` index loops. - **`10 downTo 1`** — infix function, **descending and inclusive**: 10,9,...,1. - **`a .. b step k`** — `step` (k must be > 0) sets the stride. ```kotlin for (i in 1..10) print(i) // 12345678910 for (i in 1 until 10) print(i) // 123456789 for (i in 10 downTo 1) print(i) // 10987654321 for (i in 1..10 step 2) print(i) // 13579 for (i in 10 downTo 1 step 3) print(i) // 10 7 4 1 ``` ## Types `Int`, `Long`, and `Char` support all of these (`'a'..'z'`). Floating-point only supports `..` for membership checks, **not iteration** (no integer-like progression). `1.0..2.0` is a `ClosedFloatingPointRange` you can test with `in` but cannot loop over. ## Gotchas 1. **Ascending range with start > end is empty.** `5..1` produces nothing — it does NOT count down. Use `5 downTo 1`. 2. **step must be positive.** `1..10 step -1` throws `IllegalArgumentException` at runtime. Direction is chosen by `downTo`, magnitude by `step`. 3. **Last element may differ from the bound.** With `step`, iteration stops at the largest value not exceeding the bound: `1..9 step 3` -> 1,4,7 (`.last == 7`, not 9). Always trust `progression.last`. 4. **Boundaries.** Use `.first`, `.last`, `.step`, `.count()`, `.isEmpty()` to introspect. ## Useful members ```kotlin val r = 1..100 step 5 r.first // 1 r.last // 96 r.step // 5 (5..1).isEmpty() // true ``` ## Summary Inclusive `..`, exclusive `until`, descending `downTo`, positive-magnitude `step`. Empty ascending ranges and the must-be-positive step are the classic traps.
- Can you iterate over 1.0..2.0?No. Floating-point ranges (ClosedFloatingPointRange) support the in membership check but cannot be iterated — there's no progression.
- How do you count down by 2 from 10 to 0?for (i in 10 downTo 0 step 2) — downTo sets direction, step sets the positive stride magnitude.
saying these in an interview costs you the question
- Thinking 5..1 counts downward
- Passing a negative step
- Assuming a stepped range always ends exactly on the bound
- Trying to loop over a Double range