Repetition

Beginner40 min

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

loops_sum.py
total = 0
for n in range(1, 6):
total += n
print("sum 1..5 =", total)
count = 3
while count > 0:
print("countdown", count)
count -= 1
print("liftoff")

JavaScript

loops_sum.js
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

LoopsSum.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

loops_sum.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 = 15
countdown 3
countdown 2
countdown 1
liftoff

In 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

loop_control.py
for n in range(1, 10):
if n % 2 == 0:
continue
if n > 7:
break
print("odd and at most 7:", n)

JavaScript

loop_control.js
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

LoopControl.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

loop_control.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: 1
odd and at most 7: 3
odd and at most 7: 5
odd and at most 7: 7

Even 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

halvings.py
number = 100
steps = 0
while number > 1:
number = number // 2
steps += 1
print("halvings until 1:", steps)

JavaScript

halvings.js
let number = 100;
let steps = 0;
while (number > 1) {
number = Math.floor(number / 2);
steps += 1;
}
console.log("halvings until 1:", steps);

Java

Halvings.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

halvings.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

square_search.py
guess = 1
while True:
if guess * guess > 50:
break
guess += 1
print("first number whose square exceeds 50:", guess)

JavaScript

square_search.js
let guess = 1;
while (true) {
if (guess * guess > 50) {
break;
}
guess += 1;
}
console.log("first number whose square exceeds 50:", guess);

Java

SquareSearch.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

square_search.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.1 accumulates rounding error, so x == 1.0 may never be true. Count with integers and divide.
  • Declaring the accumulator inside the loop. total = 0 inside the body resets it every time, and you are left with only the last value.
  • Reaching for break too early, when a better loop condition would say the same thing in one line.
ExerciseSum the digits

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

digit_sum.py
number = 4821
total = 0
while number > 0:
total += number % 10 # the last digit
number = number // 10 # drop the last digit
print(total)

JavaScript

digit_sum.js
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

DigitSum.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

digit_sum.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:

15

Changing number to 7 prints 7, and 1000 prints 1: the zeros contribute nothing but still take an iteration each.

ExerciseDraw a triangle

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

triangle.py
rows = 4
for row in range(1, rows + 1):
print("*" * row)

JavaScript

triangle.js
const rows = 4;
for (let row = 1; row <= rows; row++) {
let line = "";
for (let star = 0; star < row; star++) {
line += "*";
}
console.log(line);
}

Java

Triangle.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

triangle.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.

ExerciseSpot the off-by-one

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

count_to_ten.py
for n in range(1, 11):
print(n)

JavaScript

count_to_ten.js
for (let n = 1; n <= 10; n++) {
console.log(n);
}

Java

CountToTen.java
public class CountToTen {
public static void main(String[] args) {
for (int n = 1; n <= 10; n++) {
System.out.println(n);
}
}
}

C

count_to_ten.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 while loop needs a sensible start, a test and a change that moves towards stopping.
  • break leaves a loop and continue skips 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.