Counting down
The .. and until operators only count upward, so a range such as 5..-5 is empty. To count backwards, Kotlin has downTo:
for (i in 7 downTo 3) {
println(i)
}
It prints:
7
6
5
4
3
a downTo b starts at a, decreases by 1 each time, and includes b. The larger number goes on the left, the smaller one on the right. Like .., downTo includes both ends, so there is no "down until" version to worry about: the last value printed is exactly the one on the right.
What goes wrong with the wrong operator
A loop that is meant to count down but uses .. does not fail. It silently does nothing:
for (i in 5..-5) {
println(i)
}
println("finished")
This prints only finished, because the range from 5 up to -5 contains no numbers. If a reversed loop produces no output at all, check the direction first.
Combining downTo with step
You can skip values while counting down by adding step, with a positive number even though the direction is downward:
for (i in 10 downTo 1 step 3) {
println(i)
}
This prints 10, 7, 4 and 1. A negative step, such as step -3, throws an IllegalArgumentException because the direction is already decided by downTo.
The exercise: print 5 down to -5
The task is to print every number from 5 down to -5, in decreasing order, one per line. That is 11 numbers, because zero and both ends are included.
- Write the loop with the right progression. Use the variable
iand5 downTo -5. The start, 5, is the larger number, and the end, -5, is the smaller. Both appear in the output. - Print the variable. The body is
println(i), which prints one number per line.
The solution in Kotlin
for (i in 5 downTo -5) {
println(i)
}
The output is 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5, each on its own line.
Details worth knowing
- Negative numbers need no special handling. The loop passes through 0 and keeps going down. The progression is just a sequence of integers.
- The result is a progression, not a loop keyword.
5 downTo -5builds anIntProgression, an object holding the first value, the last value and the step. Theforloop then walks through it. Printing(7 downTo 3).toList()gives[7, 6, 5, 4, 3]. - Reversing an existing range. A range you already have can be flipped with
reversed():(3..7).reversed()visits 7, 6, 5, 4, 3, which is the same as7 downTo 3. - Counting an array backwards. For an array
nums, the valid indices run fromnums.size - 1down to 0, sofor (i in nums.size - 1 downTo 0)visits every index from the back. The array also has a propertynums.indices, andnums.indices.reversed()does the same.
Why not just subtract in a while loop?
You can also count down with a while loop: declare var i = 5, loop while i >= -5, print i and then do i--. It works, but you now own three pieces that must agree: the start, the condition and the update. Forget the i-- and the loop never ends; write i > -5 and the last number is lost. With 5 downTo -5 all of that is one expression, and the loop ends by itself when the values run out. Prefer the for form whenever you know the first and last value in advance.
Reading the range aloud helps: 5 downTo -5 is "from 5 down to -5". If the larger number is on the left and the smaller on the right, the direction is right.
Common mistakes
- Using
..to count down. The loop body never runs. - Swapping the operands of
downTo.-5 downTo 5is also empty, since it asks to go down from -5 to 5, and there are no numbers that do so. - Stopping one early. Because the end is included,
5 downTo -5prints -5. Writing5 downTo -4would lose it. - Using a negative step. Steps are always positive in Kotlin ranges. The direction comes from the operator.
- Looking for a
--to write. There is no update statement to change. The progression handles the decrease.
Key takeaways
a downTo bcounts down fromatobby 1 and includes both ends.a..bwitha > bis empty and does not count down.- Add
stepwith a positive number to skip values. - To print 5 through -5 in decreasing order:
for (i in 5 downTo -5) { println(i) }.