Loops that never finish
An infinite loop keeps running because its condition never becomes false. A while loop can do this in several ways. One way is a body that undoes its own progress:
var i = 0
while i < 10 {
print(i)
i -= 1
i += 1
}
Each pass prints 0, subtracts 1 and adds 1 again, so i is 0 at the start of every pass. The condition i < 10 stays true and the program prints 0 forever. The counter is updated, but the updates cancel out.
Swift's for-in loop over a range or a stride avoids this class of bug, because the loop, not your code, moves to the next value.
The exercise
The program should print the odd numbers from 15 down to 1 and then stop. The starting code is:
var number = 15
while number != 0 {
print(number)
number += 1
}
It prints 15, 16, 17 and so on without ever stopping. There are two mistakes, and both show up in the last line of the body and in the condition.
- The counter moves the wrong way.
number += 1makes the number bigger, but the target is below the start. It goes 15, 16, 17 and moves further from 0 each pass. - The condition waits for an exact value. Even with the right direction, counting down by 2 from 15 gives 13, 11, ..., 3, 1, -1. The value 0 is skipped, so
number != 0is true on every pass.
The fix
Both problems disappear if the loop is written as a for-in over a stride:
- Choose the start and the step. Start at 15 and step by -2, so every value is odd and the numbers go down.
- Choose the end. The last number to print is 1, and the task includes it, so use
through: 1. - Drop the manual update. The loop declares its own constant
numberand moves to the next value by itself. There is novarto initialise and nothing to increment.
for number in stride(from: 15, through: 1, by: -2) {
print(number)
}
The loop prints 15, 13, 11, 9, 7, 5, 3 and 1, then stops, because the next value, -1, is past the end.
Fixing it and keeping the while loop
You could keep the while loop by fixing both mistakes: subtract 2 and use a range check instead of != 0.
var number = 15
while number > 0 {
print(number)
number -= 2
}
This prints the same eight numbers. The for version is shorter and leaves no counter to forget.
Common mistakes
- Fixing only one mistake. Changing
+= 1to-= 2and keeping!= 0still never stops, because the counter skips 0. - Using
to: 1.stride(from: 15, to: 1, by: -2)excludes 1, so the last number printed is 3. - A positive step.
stride(from: 15, through: 1, by: 2)is empty because the step moves away from 1, so nothing is printed. - Changing the loop variable inside the body. The variable of a
for-inloop is a constant, sonumber -= 2inside the loop does not compile.
Key takeaways
- A loop is infinite when its condition never turns false, for instance when the counter moves the wrong way or cancels its own update.
- An
!=test can be jumped over; a range check such as> 0cannot. - A
for-inloop overstrideends on its own and manages the counter for you.
