The problem with the starting code
The goal is to print the odd numbers from 15 down to 1, which are 15, 13, 11 and so on, and then stop. The code you start with is a while loop:
var number = 15
while (number != 0) {
println(number)
number += 1
}
It prints 15, 16, 17, and keeps growing. It has two mistakes, and both come from the loop being managed by hand.
What goes wrong
- The counter moves the wrong way. The numbers should get smaller, but
number += 1makes them bigger. Each pass takes the counter further from the end of the sequence, and it also prints every number instead of every second one. - The condition waits for an exact value. The loop stops only when
numberequals 0. Even if the update werenumber -= 2, the counter would go 15, 13, ..., 3, 1, -1 and jump over 0, so the condition would stay true forever. This is the same trap as in the infinite while loop lesson.
With the code as written, an Int doesn't stop growing at its maximum. It wraps around to a large negative number, and the counter reaches 0 only after 4,294,967,281 passes. For practical purposes the program never ends.
The fix: let a range do the counting
A for loop over a range removes both mistakes. The range lists exactly the values to visit, the loop variable takes them one at a time, and the loop ends by itself after the last one. There is no condition to get wrong and no update to forget.
- Choose the start and end. The sequence starts at 15 and ends at 1, and 1 must be printed. Use
to, which includes its end value. - Choose the step. The numbers go down by 2, so the step is
-2. The negative step makes the range count down. - Write the loop. Together these give
15 to 1 by -2, andfor (number <- ...)binds each value tonumber. - Print inside the body.
println(number)is the only statement needed.
for (number <- 15 to 1 by -2) {
println(number)
}
The output is 15, 13, 11, 9, 7, 5, 3 and 1, each on its own line. After 1 the range has no more values, so the loop ends.
Why this loop cannot run forever
A range has a fixed, finite list of values, worked out from its start, end and step. Because the loop visits each value once, the number of passes can't be larger than the size of the range. The loop variable is also a fresh value on each pass that you can't reassign, so the body can't accidentally move it in the wrong direction. The only way to get an endless loop in this style would be to loop over something that is itself endless, which a range with a nonzero step is not.
Common mistakes
- Using the wrong sign for the step.
15 to 1 by 2is empty, so the loop prints nothing. - Writing
untilinstead ofto.15 until 1 by -2still ends on 1 here, because the step skips 0, but only by luck of the numbers. Usetowhen the last value should be included, so that the intent is clear. - Using a step of -1. That prints every number from 15 to 1, evens included.
- Keeping the
varand the manual update inside aforloop. The range already does that job, and a hand-written update only adds a second counter to track.
Key takeaways
- An exact-value condition such as
!= 0is fragile when the counter can step over the value. - A
forloop over a range ends by itself after the last value. start to end by step, with a negative step, counts down and includes the end if the step lands on it.
