Repetition
Repeat work with while and for loops, choose between counted and conditional loops, and avoid off-by-one errors and infinite loops.
What you will learn
- Choose between a counted loop and a conditional loop
- Write while and for loops that start, stop and step correctly
- Use break and continue, and say when each is clearer than a condition
- Reason about a loop with an invariant to avoid off-by-one errors
On this page
Computers are good at doing the same thing a million times without getting bored. A loop repeats a block of code; each run through the block is an iteration. Almost every useful program has one, whether it is summing a column, reading lines from a file or waiting for a button press.
Two kinds of loop
A conditional loop repeats while a condition is true. You may not know in advance how many times that will be: keep asking until the password is right, keep halving until the number is small.
A counted loop repeats a known number of times, or once for every item in some collection: do this ten times, do this for each name in the list.
Every language has the conditional kind (while). Counted loops are usually a separate for, and its spelling is where the languages differ most. Python and modern JavaScript loop over a range or a collection directly. C, Java and older JavaScript use the three-part form for (start; test; step).
A conditional loop is the more general tool: any counted loop can be written as a while with a counter variable. The for form exists because it puts the start, the stopping test and the step in one place where you can check them at a glance.
A first loop
This program uses both kinds. The for adds the numbers 1 to 5; the while counts down from 3.
Python
total = 0for n in range(1, 6): total += nprint("sum 1..5 =", total)
count = 3while count > 0: print("countdown", count) count -= 1print("liftoff")JavaScript
let total = 0;for (let n = 1; n <= 5; n++) { total += n;}console.log("sum 1..5 =", total);
let count = 3;while (count > 0) { console.log("countdown", count); count -= 1;}console.log("liftoff");Java
public class LoopsSum { public static void main(String[] args) { int total = 0; for (int n = 1; n <= 5; n++) { total += n; } System.out.println("sum 1..5 = " + total);
int count = 3; while (count > 0) { System.out.println("countdown " + count); count -= 1; } System.out.println("liftoff"); }}C
#include <stdio.h>
int main(void) { int total = 0; for (int n = 1; n <= 5; n++) { total += n; } printf("sum 1..5 = %d\n", total);
int count = 3; while (count > 0) { printf("countdown %d\n", count); count -= 1; } printf("liftoff\n"); return 0;}Each prints:
sum 1..5 = 15countdown 3countdown 2countdown 1liftoffIn the three-part for, int n = 1 runs once at the start, n <= 5 is tested before every iteration, and n++ (add one to n) runs after each. Python’s range(1, 6) produces 1, 2, 3, 4, 5 and does all of that bookkeeping for you.
The while loop has a pattern worth memorising. Three things must be true for it to behave: the counter starts at a sensible value (count = 3), the condition tests it (count > 0), and the body changes it (count -= 1). Leave out the last one and you have an infinite loop.
Controlling a loop from inside
Two keywords change the flow within the body. continue abandons the current iteration and jumps to the next. break ends the loop altogether.
This loop skips even numbers with continue and quits early with break:
Python
for n in range(1, 10): if n % 2 == 0: continue if n > 7: break print("odd and at most 7:", n)JavaScript
for (let n = 1; n < 10; n++) { if (n % 2 === 0) { continue; } if (n > 7) { break; } console.log("odd and at most 7:", n);}Java
public class LoopControl { public static void main(String[] args) { for (int n = 1; n < 10; n++) { if (n % 2 == 0) { continue; } if (n > 7) { break; } System.out.println("odd and at most 7: " + n); } }}C
#include <stdio.h>
int main(void) { for (int n = 1; n < 10; n++) { if (n % 2 == 0) { continue; } if (n > 7) { break; } printf("odd and at most 7: %d\n", n); } return 0;}All four print:
odd and at most 7: 1odd and at most 7: 3odd and at most 7: 5odd and at most 7: 7Even values hit continue and never reach the print. At n = 9 the break ends the loop. Notice the stop value: range(1, 10) and n < 10 both stop before 10.
A conditional loop is the right tool when the number of passes is not known in advance. How many times can you halve 100 before reaching 1?
Python
number = 100steps = 0while number > 1: number = number // 2 steps += 1print("halvings until 1:", steps)JavaScript
let number = 100;let steps = 0;while (number > 1) { number = Math.floor(number / 2); steps += 1;}console.log("halvings until 1:", steps);Java
public class Halvings { public static void main(String[] args) { int number = 100; int steps = 0; while (number > 1) { number = number / 2; steps += 1; } System.out.println("halvings until 1: " + steps); }}C
#include <stdio.h>
int main(void) { int number = 100; int steps = 0; while (number > 1) { number = number / 2; steps += 1; } printf("halvings until 1: %d\n", steps); return 0;}All four print halvings until 1: 6 (100, 50, 25, 12, 6, 3, 1). The last loop shape is while true with a break: loop until a condition inside the body says stop.
Python
guess = 1while True: if guess * guess > 50: break guess += 1print("first number whose square exceeds 50:", guess)JavaScript
let guess = 1;while (true) { if (guess * guess > 50) { break; } guess += 1;}console.log("first number whose square exceeds 50:", guess);Java
public class SquareSearch { public static void main(String[] args) { int guess = 1; while (true) { if (guess * guess > 50) { break; } guess += 1; } System.out.println("first number whose square exceeds 50: " + guess); }}C
#include <stdio.h>
int main(void) { int guess = 1; while (1) { if (guess * guess > 50) { break; } guess += 1; } printf("first number whose square exceeds 50: %d\n", guess); return 0;}All four print first number whose square exceeds 50: 8.
Use break and continue for exceptional cases, not as the main structure. If a loop has three breaks scattered through it, a clearer condition at the top is usually better. The while true loop is the honest exception: when the natural place to decide is the middle of the body, an explicit exit is easier to read than contorting the condition.
Off-by-one errors and invariants
The most common loop bug has a name: the off-by-one error. You loop one time too many or too few. In the continue example above, n < 10 stopped before 10, because in most languages’ ranges the start is included and the stop is excluded. Python’s range(1, 5) gives 1 to 4. Writing n <= 5 versus n < 5 in the three-part form is the same decision.
A habit that prevents these: state the loop’s invariant, a sentence that is true before the loop starts and after every iteration. In the for loop that sums 1 to 5, the invariant is “total is the sum of the numbers handled so far”. Before the loop it holds trivially (nothing handled, total 0). Each iteration keeps it true. When the loop ends, “handled so far” means “all of them”, so the invariant becomes the answer. If you cannot state an invariant, you probably do not yet know what the loop is for.
A second habit: test the smallest cases. What does the loop do for zero iterations? For exactly one?
Infinite loops
If the condition never becomes false, the program never ends. Sometimes this is intended (a server waits for requests forever). Usually it is a bug: the counter was never updated, or it moves away from the stopping value. If a program is stuck in the terminal you met in tools of the trade, press Ctrl+C to interrupt it.
Common pitfalls
- Off by one. Is the stop value included? Check against the smallest case.
- Forgetting to update the loop variable in a
while, which never ends. - Changing a collection while looping over it. Adding or removing items mid-loop skips elements or loops forever. Build a new collection instead.
- Using a floating-point counter. A step of
0.1accumulates rounding error, sox == 1.0may never be true. Count with integers and divide. - Declaring the accumulator inside the loop.
total = 0inside the body resets it every time, and you are left with only the last value. - Reaching for
breaktoo early, when a better loop condition would say the same thing in one line.
Given a positive whole number such as 4821, compute the sum of its digits (4 + 8 + 2 + 1 = 15) without converting it to text. Hint: n % 10 is the last digit, and dividing by 10 with whole-number division removes it. Input: 4821. Expected output: 15. Also try 7 (expected 7) and 1000 (expected 1).
Show solution
We do not know in advance how many digits there are, so a conditional loop fits.
Python
number = 4821total = 0while number > 0: total += number % 10 # the last digit number = number // 10 # drop the last digitprint(total)JavaScript
let number = 4821;let total = 0;while (number > 0) { total += number % 10; // the last digit number = Math.floor(number / 10); // drop the last digit}console.log(total);Java
public class DigitSum { public static void main(String[] args) { int number = 4821; int total = 0; while (number > 0) { total += number % 10; // the last digit number = number / 10; // drop the last digit } System.out.println(total); }}C
#include <stdio.h>
int main(void) { int number = 4821; int total = 0; while (number > 0) { total += number % 10; /* the last digit */ number = number / 10; /* drop the last digit */ } printf("%d\n", total); return 0;}The invariant: total holds the sum of the digits already removed from number. The loop stops when no digits remain. All four print:
15Changing number to 7 prints 7, and 1000 prints 1: the zeros contribute nothing but still take an iteration each.
Print a right-angled triangle of stars with 4 rows: one star in the first row, two in the second, and so on. The input is rows = 4. Expected output:
**********Show solution
The number of rows is known, so use a counted loop. The row number doubles as the number of stars. Python can repeat a string with "*" * row; the other three languages put a second counted loop inside the first, printing one star per pass and a newline at the end of each row.
Python
rows = 4for row in range(1, rows + 1): print("*" * row)JavaScript
const rows = 4;for (let row = 1; row <= rows; row++) { let line = ""; for (let star = 0; star < row; star++) { line += "*"; } console.log(line);}Java
public class Triangle { public static void main(String[] args) { int rows = 4; for (int row = 1; row <= rows; row++) { for (int star = 0; star < row; star++) { System.out.print("*"); } System.out.println(); } }}C
#include <stdio.h>
int main(void) { int rows = 4; for (int row = 1; row <= rows; row++) { for (int star = 0; star < row; star++) { printf("*"); } printf("\n"); } return 0;}All four print the four rows shown above.
A program is meant to print the numbers 1 to 10, one per line, but prints only 1 to 9. The loop header is shown in each language. Fix it, then explain why 10 was missing. Expected output after the fix: ten lines, 1 to 10.
Python
for n in range(1, 10): print(n)JavaScript
for (let n = 1; n < 10; n++) { console.log(n);}Java
for (int n = 1; n < 10; n++) { System.out.println(n);}C
for (int n = 1; n < 10; n++) { printf("%d\n", n);}Show solution
The stop value is excluded. In Python use range(1, 11). In JavaScript, Java and C change the test to n <= 10 (or n < 11). A quick check on a smaller case helps: range(1, 3) gives 1 and 2, and n < 3 does the same, so to include k you must stop at k + 1 or test <= k.
Python
for n in range(1, 11): print(n)JavaScript
for (let n = 1; n <= 10; n++) { console.log(n);}Java
public class CountToTen { public static void main(String[] args) { for (int n = 1; n <= 10; n++) { System.out.println(n); } }}C
#include <stdio.h>
int main(void) { for (int n = 1; n <= 10; n++) { printf("%d\n", n); } return 0;}All four print 1 to 10, one per line.
Summary
- A loop repeats a block; each run is an iteration.
- Use a counted loop when you know the number of passes or have a collection to walk through, and a conditional loop when you do not.
- A
whileloop needs a sensible start, a test and a change that moves towards stopping. breakleaves a loop andcontinueskips to the next iteration; use them sparingly.- Stating the loop invariant and testing zero and one iterations catches most off-by-one errors.
Next: Functions, where you give a block of code a name and reuse it.