On this page

Recursion

A function can call any function the compiler knows about. That includes itself. A function that calls itself is called recursive, and writing one is called recursion.

Recursion adds no new piece of C++. Every statement in this lesson is one you have written before. What is new is a way of reading a call: as a job that is handed over, while the caller waits for it to come back.

Calls that wait

Start with functions that call other functions. This program makes breakfast in three layers.

#include <iostream>

void spread_butter(int slice) {
    std::cout << "    butter on slice " << slice << "\n";
}

void make_toast(int slices) {
    std::cout << "  toasting " << slices << " slices\n";
    for (int slice = 1; slice <= slices; ++slice) {
        spread_butter(slice);
    }
    std::cout << "  toast is ready\n";
}

void make_breakfast() {
    int slices = 2;
    std::cout << "breakfast starts\n";
    make_toast(slices);
    std::cout << "breakfast is ready\n";
}

int main() {
    make_breakfast();
    std::cout << "eat\n";
}
breakfast starts
  toasting 2 slices
    butter on slice 1
    butter on slice 2
  toast is ready
breakfast is ready
eat

Module 6 said that a caller stops at a call and carries on when the called function has finished. Look at what that means when calls are inside calls. While spread_butter prints its line, three other functions have started and not finished. Each of them is waiting at the line of a call.

Here is every call in progress at the moment butter on slice 2 is printed.

Call What it is doing Its variables
main waiting at make_breakfast(); none
make_breakfast waiting at make_toast(slices); slices is 2
make_toast waiting at spread_butter(slice); slices is 2, slice is 2
spread_butter running slice is 2

This list is the call stack. It is called a stack because it behaves like a stack of plates: a new call goes on top, and only the call on top can finish and be taken off. The call that started last ends first.

Three things to notice.

  • Only the call on top is running. Every call below it is waiting for the one above.
  • A waiting call keeps its variables. make_toast still has its slice, and when spread_butter returns, the loop carries on from where it stopped.
  • When a call ends, it leaves the stack and its variables are gone. The call below it carries on at the statement after the call.

You can see the call stack in the lab. When you press Step through and step forward, the lab lists the calls in progress beside the program, each with its own variables, and marks every call except the running one as waiting. If you paste this program into the lab and step until butter on slice 2 appears, the list matches the table above. The first step of the lab asks you to read that list for another program.

A function that calls itself

A call puts a new entry on the stack, with new variables. Nothing in that rule says the called function must be a different one. This function calls itself.

#include <iostream>

void descend(int floor) {
    if (floor == 0) {
        std::cout << "ground floor\n";
        return;
    }
    std::cout << "floor " << floor << "\n";
    descend(floor - 1);
}

int main() {
    descend(3);
    std::cout << "outside\n";
}
floor 3
floor 2
floor 1
ground floor
outside

Follow it call by call.

  1. main calls descend(3). Its parameter floor is 3. The condition is false, so it prints floor 3 and calls descend(2). Now it waits.
  2. descend(2) is a new call with its own floor, which is 2. It prints floor 2, calls descend(1) and waits.
  3. descend(1) prints floor 1, calls descend(0) and waits.
  4. descend(0) has a floor of 0. The condition is true: it prints ground floor and returns.
  5. descend(1) carries on after its call. There is nothing left in its body, so it ends. Then descend(2) ends, then descend(3), and main prints outside.

The function was called four times, and at step 4 all four calls were in progress at once.

Call What it is doing Its floor
main waiting none
descend waiting 3
descend waiting 2
descend waiting 1
descend running 0

There is one definition of descend, and there are four variables named floor. That is the rule from module 6: every call gets its own parameters and local variables. Two calls of the same function share nothing but the text of the body.

This is not a loop. Nothing jumps back to the top of the body, and no variable is counted down: no statement ever changes a floor. Each call hands a smaller number to a new call.

Stepped through in the lab, this program shows the list of calls growing by one descend at a time, each with its own floor, and then shrinking again.

The case that stops

Every recursive function has two parts, and you can find both in descend.

  • The base case is the input that is small enough to answer without another call. For descend it is a floor of 0.
  • The recursive case is everything else. It does one small part of the job and gives the rest, which is a smaller version of the same job, to a call of the same function.

The function tests for the base case first. Then it is certain that the recursive case only runs when there is still something to hand on.

A recursive function can return a value too. This one adds up the whole numbers from 1 to n.

#include <iostream>

// Needs: n is 0 or more.
// Returns: 1 + 2 + ... + n, and 0 when n is 0.
int sum_to(int n) {
    if (n == 0) {
        return 0;
    }
    return n + sum_to(n - 1);
}

int main() {
    std::cout << sum_to(4) << "\n";
    std::cout << sum_to(100) << "\n";
}
10
5050

Read the two parts as two sentences.

  • Base case: the sum of no numbers is 0.
  • Recursive case: the sum up to n is n plus the sum up to n - 1.

Both sentences are true, and that is all you need to check. To write the recursive case, suppose the smaller call already works. If sum_to(3) gives 6, then sum_to(4) must give 4 + 6. You do not have to follow the calls all the way down each time, any more than you read the body of circle each time you draw one.

Two questions decide whether a recursion is sound.

  1. Is there a base case that answers without calling the function again?
  2. Does every recursive call get an input that is nearer to the base case?

In sum_to the recursive call gets n - 1. Starting from any n that is 0 or more, subtracting 1 again and again must arrive at 0. The contract says so in its first line: for a negative n the function promises nothing, because counting down from -1 never meets 0. The last section of this lesson shows what happens then.

Before the call, and after it

A recursive call sits somewhere in the body. Statements before it run while the calls are being made, on the way down. Statements after it run when the calls come back, on the way up, and the calls come back in the opposite order.

These two functions print the digits of a number. They differ in one thing: which comes first, the print or the call.

#include <iostream>

void last_digit_first(int n) {
    if (n < 10) {
        std::cout << n << "\n";
        return;
    }
    std::cout << n % 10 << "\n";
    last_digit_first(n / 10);
}

void first_digit_first(int n) {
    if (n < 10) {
        std::cout << n << "\n";
        return;
    }
    first_digit_first(n / 10);
    std::cout << n % 10 << "\n";
}

int main() {
    last_digit_first(472);
    std::cout << "--\n";
    first_digit_first(472);
}
2
7
4
--
4
7
2

Both functions make the same three calls, with 472, then 47, then 4. n % 10 is the last digit of n, and n / 10 is n without it.

In last_digit_first each call prints its digit and then makes the next call. The call with 472 prints first, so the 2 comes first.

In first_digit_first each call makes the next call first and prints when that call has come back. So the call with 472 prints last.

Call Starts Prints its digit
first_digit_first(472) first third: 2
first_digit_first(47) second second: 7
first_digit_first(4) third first: 4

While the call with 4 was running, the call with 472 was waiting on the stack. It kept its own n the whole time. When its turn came, n % 10 was still 2.

This is what the stack gives you that a loop over the digits does not. A loop that takes digits off with % 10 meets them last digit first. To print them in reading order it would have to store them somewhere. Here the waiting calls store them.

Writing out the calls

When a recursive function returns a value, you can check it on paper. Write each call on a line of its own, indented under the call that made it, until you reach a base case. Then fill in the returned values from the bottom up.

#include <iostream>

// Needs: n is 1 or more.
// Returns: how many times n can be halved, dropping fractions, before it is 1.
int halvings(int n) {
    if (n == 1) {
        return 0;
    }
    return 1 + halvings(n / 2);
}

int main() {
    std::cout << halvings(20) << "\n";
    std::cout << halvings(1) << "\n";
}
4
0

Written out, the calls for halvings(20) look like this.

halvings(20)  is  1 + halvings(10)
  halvings(10)  is  1 + halvings(5)
    halvings(5)  is  1 + halvings(2)
      halvings(2)  is  1 + halvings(1)
        halvings(1)  is  0          the base case

Going down, nothing can be added yet. Every line ends in a call whose value is not known. The first value that is known is the last line's.

Now go back up. Each call takes the value from the line below it and finishes its own addition.

Call Gets back Returns
halvings(1) nothing: it is the base case 0
halvings(2) 0 1
halvings(5) 1 2
halvings(10) 2 3
halvings(20) 3 4

The returns happen in that order, the deepest call first. The call you wrote first returns last. At the deepest point five calls of halvings are in progress, four waiting and one running.

Stepping through shows the same thing. Each time a call ends, the lab says which call it was and what it returned, and that call leaves the list. Write the calls out on paper first, then step and compare. If the two differ, you have found something you misread.

Module 11 had a function with this name too, written with a loop, and it measured binary search: each look halves the range that is left. The two do not give the same number. Module 11's version went on halving until nothing was left, so it gives 5 for 20, the most elements a binary search of 20 can look at. This one stops when it reaches 1, one halving sooner, and gives 4.

Two calls each

So far each call made at most one more call, and the calls formed a chain. A function may call itself more than once. This one cuts a plank in half, and each half in half again, down to pieces of length 1. It prints each piece, indented by how many cuts it took to get there.

#include <iostream>
#include <string>

void cut(int length, const std::string& indent) {
    std::cout << indent << length << "\n";
    if (length == 1) {
        return;
    }
    int left = length / 2;
    cut(left, indent + "  ");
    cut(length - left, indent + "  ");
}

int main() {
    cut(4, "");
}
4
  2
    1
    1
  2
    1
    1

The output is a picture of the calls: each line is one call, written under the call that made it. Every call that is not a base case makes two calls, so the calls branch. Such a picture is called a tree of calls. The first call is its root, and the base cases are its ends.

Read the order of the lines carefully.

  • cut(4) prints 4 and makes its first call, cut(2). It now waits.
  • That cut(2) prints 2, makes its first call cut(1), waits, then makes its second call cut(1), waits again, and ends.
  • Only now does cut(4) make its second call.

The whole of the first branch is finished before the second branch starts. The calls do not happen side by side: there is still one call stack, and one running call.

Count two different things in this tree.

  • How many calls in all? One of length 4, two of length 2, four of length 1: seven. A plank of 8 gives 15 calls, and a plank of 1024 gives 2047. Each extra level doubles the number of calls in that level.
  • How many calls in progress at once? Never more than three: a 4, a 2 and a 1. The stack holds one path from the root to the running call, not the whole tree.

A function that makes two calls for each call can do a great deal of work for a small input. Counting the calls, as you counted comparisons in module 11, tells you how much.

A loop or a recursion?

Any job a loop can do, a recursion can do, and the other way round. Here is sum_to from earlier in this lesson, next to the loop version that module 6 used.

#include <iostream>

int sum_to(int n) {
    if (n == 0) {
        return 0;
    }
    return n + sum_to(n - 1);
}

int sum_to_loop(int n) {
    int total = 0;
    for (int i = 1; i <= n; ++i) {
        total += i;
    }
    return total;
}

int main() {
    std::cout << sum_to(6) << " " << sum_to_loop(6) << "\n";
    std::cout << sum_to(0) << " " << sum_to_loop(0) << "\n";
}
21 21
0 0

The same parts are in both, in different places.

The loop The recursion
Where it stops the test i <= n becomes false the base case n == 0
What moves it on the update ++i the smaller argument n - 1
Where the answer builds up one variable, total, changed on every turn the return values, one addition in each call
Variables two, for the whole job one n for each call, never changed

Which one should you write?

  • A job that is "do this n times" or "do this for each element" is a loop. The loop is shorter to read, and it uses one set of variables however large n is. The recursion keeps one waiting call on the stack for every step, and each waiting call takes some memory.
  • A job that contains smaller copies of itself is a recursion, above all when there is more than one copy. cut is five lines. Written with loops it would need a vector of the pieces still to be cut, managed by hand. That vector would be doing the work of the call stack.

To turn a recursion that makes one call into a loop, find what changes from one call to the next. That becomes the variable the loop updates. The base case, turned round, becomes the loop's test.

When it never stops

A recursion stops because its calls reach the base case. Take the base case away and nothing stops it. Do not run this function.

void descend(int floor) {
    std::cout << "floor " << floor << "\n";
    descend(floor - 1);
}

descend(3) prints floor 3, floor 2, floor 1, floor 0, floor -1 and carries on. Each call waits for the next, so no call ever ends, and each waiting call keeps its place on the stack.

A loop that never ends runs until somebody stops it. A recursion that never ends is different: the memory set aside for the call stack is limited, and after many thousands of calls it is full. This is a stack overflow. The program is ended at once. In the lab you get a message that the program stopped while it was running, and the output shows only what was printed up to then. It is the second kind of error from module 7: the program compiles, and stops while running.

A base case that exists is not enough. The calls must reach it. This function has one, and can still step over it.

int count_pairs(int n) {
    if (n == 0) {
        return 0;
    }
    return 1 + count_pairs(n - 2);
}

count_pairs(6) makes calls with 6, 4, 2 and 0, and returns 3. count_pairs(5) makes calls with 5, 3, 1, -1, -3 and so on. The value 0 is never met, and the stack overflows.

There are two repairs, and a careful function has both.

  • Make the base case wide enough to catch everything at or beyond the end: if (n <= 0) in place of if (n == 0).
  • Write the contract, as in module 7. // Needs: n is 0 or more and even. tells the caller which inputs are allowed, and tells you which inputs your base case must be reachable from.

When a recursive program stops in this way, ask the two questions from earlier in this lesson, in order. Is there a base case? Does every recursive call move towards it, for every input the contract allows?

Your turn

Now try it yourself

Predict what a program will do, run it, and step through it line by line. The checks tell you when your own code works.

Loading the lab…

In this lab

  1. Calls that wait

    main calls a, a calls b, and b calls c. Each function prints one line when it starts and one line as its last statement. Decide the order of the six lines before you run it.

    When you have answered, press Step through this snippet and step until c starts has been printed. Read the list of calls in progress beside the program: which are marked as waiting?

  2. The case that stops

    countdown prints its parameter and then calls itself with a number that is one smaller. An if at the top tests for 0.

  3. Count up instead

    print_numbers has the same body as countdown in the step before. Run it: it prints 3, 2, 1.

    Make it print 1, 2, 3, each on its own line, by moving one statement. Do not add a loop, do not add a variable, and do not change main.

    Before you move anything, decide: when does the call with n equal to 1 print, compared with the call where n is 3?

    Afterwards press Step through. Watch the list of calls grow to four calls of print_numbers before the first number is printed.

  4. Write out the calls

    factorial(n) is the product of the whole numbers from 1 to n. On paper, write out the calls that factorial(4) makes, each indented under the one that made it. Then fill in what each returns, from the bottom up. Answer from your paper.

    When you have answered, press Step through this snippet. Each time a call ends, the lab says which call it was and what it returned. Compare the order with your paper.

  5. Two calls each

    fib(n) calls itself twice, with n - 1 and with n - 2, unless n is below 2.

    On paper, draw the tree of calls for fib(5): write fib(5) at the top, under it the two calls it makes, under each of those the calls it makes, and stop at every fib(1) and fib(0). Count from your drawing.

    When you have answered, press Step through this snippet. Follow the list of calls in progress: it shows one path through your tree at a time.

  6. The same job as a loop

    sum_digits adds up the digits of a number with recursion: the last digit, plus the sum of the digits of the rest.

    sum_digits_loop should do the same job with a while loop and no call to either function. At the moment it returns 0. Write its body.

    main reads numbers until the input ends and prints both results for each, so you can see whether they agree. For the input 7 52 909 4096 the program should print

    7: 7 7
    52: 7 7
    909: 18 18
    4096: 19 19
    

    Leave sum_digits and main as they are.

  7. power

    Write a recursive function above main.

    First line: int power(int base, int exponent)

    Needs: exponent is 0 or more.

    Returns: base multiplied by itself exponent times. So power(2, 5) is 32 and power(7, 1) is 7. A number to the power 0 is 1: power(2, 0) is 1.

    Use no loop and no library function. Before you write, say the two parts as sentences: what is the answer when there is nothing to multiply, and how does power(base, exponent) follow from a call with a smaller exponent?

    The function returns its answer and prints nothing.

  8. Circles inside circles

    Write a recursive function that draws a ring, then two smaller copies of the whole pattern inside it, side by side.

    First line: void draw_rings(int x, int y, int radius)

    What it does:

    1. If radius is less than 10, it draws nothing and returns.
    2. It draws a ring centred on (x, y): a circle of radius radius in "navy", and then, on top of it and with the same centre, a circle of radius radius - 4 in "white".
    3. It draws the same pattern with radius radius / 2, centred on (x - radius / 2, y). That is the left half of the ring.
    4. It draws the same pattern with radius radius / 2, centred on (x + radius / 2, y). That is the right half.

    The divisions are int divisions, so a radius of 25 gives 12.

    The order matters: a ring drawn after the rings inside it would cover them.

    main is given. With biggest = 160 the picture has 31 rings. Try a biggest of 20 and press Step through: three calls draw a ring, and four more calls return at once. Watch which of them is running, and which are waiting, as each circle appears.

  9. Towers of Hanoi

    Three pegs stand in a row. On one of them is a pile of n discs of different sizes, the largest at the bottom. Disc 1 is the smallest and disc n the largest. The puzzle is to move the whole pile to another peg.

    • Only one disc moves at a time, from the top of one peg to the top of another.
    • A disc may never be put on a smaller disc.

    The plan for moving n discs from one peg to another contains two smaller copies of itself:

    1. move the n - 1 discs above the largest one out of the way, onto the spare peg;
    2. move disc n to the peg it has to go to;
    3. move the n - 1 discs from the spare peg on top of it.

    First line: void hanoi(int n, char from, char to, char spare)

    Needs: n is 1 or more. The three chars are the names of the three pegs.

    What it prints: one line for each move, in the order the moves are made, in exactly this form:

    Move disc 1 from A to C
    

    The number is the disc that moves, then come the name of the peg it leaves and the name of the peg it lands on. hanoi(1, 'A', 'C', 'B') prints that one line.

    In steps 1 and 3 of the plan the three pegs change roles. Decide for each of the two calls which peg is its from, which its to and which its spare.

    The checks call hanoi with other peg names and other numbers of discs. They count the lines and then play your moves on three pegs to see that every move is allowed and that the pile ends up on to.

  10. Binary search, again

    In module 11 you wrote binary search with a while loop that moved low and high towards each other, and you gave it a counter to see how much work it did. Write it again with no loop. The range that is still to be searched is handed to the next call, and so is the counter.

    First line: int search(const std::vector<int>& v, int wanted, int low, int high, int& calls)

    Needs: v is sorted into increasing order. low and high are positions in v, or low is greater than high, which means the range is empty.

    Returns: a position of wanted among the positions from low to high, both included, or -1 if it is not there.

    Counts: every call of search adds 1 to calls as its first statement, whatever that call goes on to do. The function only ever adds to the counter. Setting it to 0 is the caller's job, as in module 11. A call that makes another call hands it the same counter.

    Each call looks at one element, the one in the middle of its range. Either that is the answer, or the call returns what a call with half the range returns.

    There are two base cases here. Say what they are before you write.

    main is given. It should print

    4 in 3 calls
    -1 in 4 calls
    

    The checks call search with ranges of every size and with part of a vector as the range. Then they search a sorted vector of 2,000 elements and read the counter. Halving empties that range in 12 calls or fewer, and the checks allow 14. A search that drops one element for each call can need 2,000.

  11. Down and up

    Write one recursive function above main.

    First line: void down_up(int n)

    Needs: n is 0 or more.

    What it prints: the numbers from n down to 1 and then from 1 up to n, each on its own line. down_up(3) prints

    3
    2
    1
    1
    2
    3
    

    down_up(0) prints nothing.

    Use no loop and no second function. The recursive case has three statements: a print, a call, and a print.

  12. reverse_text

    Write a recursive function above main.

    First line: std::string reverse_text(const std::string& text)

    Returns: the characters of text in the opposite order. reverse_text("lamp") is "pmal".

    The rule to turn into code: the reverse of a text is the reverse of everything after its first character, followed by the first character. text.substr(1) is everything after the first character.

    Decide which text is small enough to need no call. Use no loop.

  13. gcd, two ways

    The greatest common divisor of two whole numbers is the largest number that divides both. For 84 and 36 it is 12.

    Euclid's rule finds it without trying divisors:

    • the greatest common divisor of a and 0 is a;
    • otherwise, the greatest common divisor of a and b is the greatest common divisor of b and a % b.

    So for 84 and 36 the pairs are (84, 36), then (36, 12), then (12, 0), and the answer is 12.

    Write the rule twice, above main.

    int gcd(int a, int b) is recursive and has no loop.

    int gcd_loop(int a, int b) uses a while loop and calls no function.

    Needs, for both: a and b are 0 or more, and not both 0.

    main reads two numbers and prints both results.

  14. sum_from

    Write a recursive function above main.

    First line: int sum_from(const std::vector<int>& v, int start)

    Needs: start is at least 0 and at most the number of elements of v.

    Returns: the sum of the elements from position start to the end of the vector. When start equals the number of elements there is nothing left to add, and the sum is 0.

    So with v = {4, -1, 7, 10}, sum_from(v, 0) is 20, sum_from(v, 2) is 17 and sum_from(v, 4) is 0.

    Use no loop. The vector is passed by const reference, so every call looks at the same vector and nothing is copied. What changes from one call to the next is start.

    v.size() is an unsigned count. Store it in an int before you compare it with start, as in module 8.

  15. is_palindrome, recursively

    In module 8 you wrote is_palindrome with a loop. Here is the same question as a recursion.

    First line: bool is_palindrome(const std::string& text)

    Returns: true when text reads the same in both directions. Characters are compared exactly as they are, so "Anna" is not a palindrome here and "anna" is.

    The rule to turn into code: a text is a palindrome when its first and last characters are equal, and what lies between them is a palindrome. A text of no characters or of one character is a palindrome.

    text.substr(1, text.size() - 2) is the text without its first and last characters. Make sure it is only worked out for a text of at least two characters.

    Use no loop. The function returns its answer and prints nothing.

  16. A carpet

    Write a recursive function that draws a square carpet with a hole in the middle, where each of the eight parts around the hole is a smaller carpet.

    First line: void carpet(int x, int y, int size, int depth)

    Needs: depth is 0 or more, and size can be divided by 3 depth times without a remainder.

    What it does:

    • If depth is 0, it draws one filled square in "purple": its top-left corner is at (x, y), and it is size wide and size high.
    • Otherwise it draws no rectangle itself. Think of the square as three rows of three smaller squares, each size / 3 wide. The smaller square in row row and column col, both counted from 0, has its top-left corner at (x + col * size / 3, y + row * size / 3). For each of them except the middle one (row 1, column 1), it calls carpet with that corner, the size size / 3, and depth - 1.

    So a carpet of depth 1 is 8 squares around an empty middle, and a carpet of depth 2 is 64 squares.

    This function makes eight calls for each call. You may make them with two loops, one inside the other, around a single call: recursion and loops can be used together.

    The picture holds at most 500 shapes, so keep depth at 2 or below: depth 3 would need 512 squares.

  17. The tree of calls

    This one is done on paper first. You need your hanoi function from the lab.

    1. Draw every call that hanoi(3, 'A', 'C', 'B') makes, as a tree. Write the first call at the top. Under each call, write the calls it makes, left to right in the order it makes them, with their arguments: the number of discs and the three pegs in the order from, to, spare.
    2. Beside each call, write the line that this call prints itself, if it prints one.
    3. Number those lines from 1 upwards in the order in which they are printed. Remember that a call prints its own line between its two calls.
    4. Paste your hanoi function into the program below, press Step through, and follow the list of calls in progress. Each time a line is printed, find the call that printed it in your tree.

    Nothing checks this one. When you have finished, go through the list below.