Infinite loops in a for header
A for loop ends when its condition becomes false. It is infinite when nothing ever makes the condition false. The header makes this easy to see, but also easy to get wrong, because the update, which runs after each pass, can quietly undo your progress or move the loop variable in the wrong direction.
Example: undoing the update
for (let i = 0; i < 10; i++) {
console.log(i);
i--;
}
Each pass prints i, then the body subtracts 1, and then the header adds 1. The two changes cancel out. The variable is 0 at the start of every pass, so i < 10 stays true, and the program prints 0 forever. Changing the loop variable inside the body of a for loop is a common source of this bug. Let the header be the only place that moves it.
The exercise: two mistakes in one header
The starting code should print the odd numbers from 15 down to 1 and then stop. Instead it prints 15, 16, 17 and keeps getting bigger:
for (let number = 15; number != 0; number++) {
console.log(number);
}
There are two separate problems, both in the header.
Mistake 1: the update goes the wrong way
The loop has to count down, but number++ adds 1. The variable moves away from the end of the range, so it can never reach the point where the loop stops. It also adds 1, but the task wants only the odd numbers 15, 13, 11 and so on, which means moving by 2. The update should be number -= 2.
Mistake 2: the condition waits for a value that never comes
With the update fixed, the condition number != 0 is still wrong. Starting at 15 and subtracting 2 gives 15, 13, 11, 9, 7, 5, 3, 1, -1, -3, and so on. Every one of these is odd, so the counter jumps from 1 to -1 and never equals 0. The condition stays true for ever. The fix is a range check. The loop should continue while the number is positive, so the condition is number > 0. When the counter reaches -1, the condition is false, and the loop ends.
The solution in TypeScript
for (let number = 15; number > 0; number -= 2) {
console.log(number);
}
It prints 15, 13, 11, 9, 7, 5, 3 and 1, and then stops. Only the condition and the update changed. The initialization, let number = 15, was already correct.
How to find the bug yourself
- Look at the update first. Does it move the variable toward the limit in the condition? A loop that has to end below a value needs the variable to go down, and a loop that has to end above a value needs it to go up.
- Look at the condition next. Could the variable step over the value it tests for? If yes, use
>,>=,<or<=instead of!=or!==. - Trace the first few and last few passes by hand. Write down the value of the variable at each check of the condition.
- Check the body. Make sure nothing there changes the loop variable in a way that cancels the update.
Common mistakes
- Fixing only one of the two mistakes. With
number -= 2andnumber != 0, the loop runs forever downward. Withnumber++andnumber > 0, it runs forever upward. Both parts of the header have to be right. - Using
number--. The loop then ends, but it prints every number from 15 to 1, including the even ones. The task wants a step of 2. - Using
number >= 0. For this start value and step the output is identical, but the condition describes the task less precisely. The task asks for positive numbers. - Changing the start. The start of 15 is correct. Moving it to an even number would change which numbers are printed.
Note that the name number is allowed as a variable name in TypeScript, because it is a type name only, not a reserved word. In your own code, a more descriptive name is usually better.
Key takeaways
- A
forloop is infinite when its condition never becomes false. - The update must move the loop variable toward the limit, and the body should not undo it.
- When the step can jump over the stop value, test with
>,>=,<or<=rather than an exact value. - Check the direction of the update and the form of the condition together.