Foundations · 15 min

Recursion: watch the call stack

Step through recursive code line by line and see every call get its own frame, wait, and return.

01Why it matters

A function that calls itself

Some problems contain smaller copies of themselves. The factorial of 4 is 4 × 3 × 2 × 1, which is just 4 times the factorial of 3. So a function can answer "what is factorial(4)?" by asking itself "what is factorial(3)?", and so on down, until the question is small enough to answer outright.

That raises a fair question: if factorial is still running when it calls factorial again, where does the first call keep its n while the second one runs? The answer is the call stack, and once you can see it, recursion stops feeling like magic.

02Key parts

The pieces you will see

base case
An input small enough to answer directly, without calling again. Every recursion needs one, or it never stops.
recursive case
An input answered by calling the same function on a smaller input, then using its answer.
frame
The memory for one call: its parameters and local variables, and where it is in the code.
call stack
The frames of every call that hasn't returned yet. A new call adds a frame on top; a return removes it.

03See it work

Watch the stack grow and shrink

Run it, then step through. On the left is the call stack: the global frame, then one frame per call that is still running. Each factorial frame has its own n. The calls pile up until factorial(1) hits the base case; then each frame returns its answer to the one below and disappears.

def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)

result = factorial(4)
print(result)

04Predict

Predict: how many lists?

This function adds up a list by taking the first item and adding the total of the rest. Each call passes the list on with a bigger i.

def total(nums, i):
    if i == len(nums):
        return 0
    return nums[i] + total(nums, i + 1)

values = [3, 1, 4]
print(total(values, 0))

05Try it

See every stack at once: the call tree

When a function calls itself more than once, as Fibonacci does, the stack goes up and down many times. Drawing every call as a node, joined to the call that made it, gives the call tree. At any moment, the call stack is the path from the top call down to the call that is running.

Try n = 6 and n = 7: each step up multiplies the calls by about 1.6, the waste the memoization lesson fixes.

06Check yourself

Check your understanding

  1. Question 1 of 4

    What does the base case do?

    Your answer comes back in Review in a few days, so it sticks.

  2. Question 2 of 4

    The top-level code calls factorial(5). At the deepest point, how many frames are on the stack, counting the global frame?

  3. Question 3 of 4

    In what order do the calls made by factorial(4) return?

  4. Question 4 of 4

    half(n) returns 0 when n is 1, and otherwise returns 1 + half(n − 2). What does half(4) do?

07Explain it back

Say it in your own words

How does a recursive function like factorial work out its answer, and how does each call keep track of its own n?

08Recap

Recap

  • A recursive function answers a big input by calling itself on a smaller one, and needs a base case to stop.
  • Every call gets its own frame, so the caller's variables are safe while it waits.
  • The call stack holds the calls still running: the newest on top, returning first.
  • Passing a list passes a reference: every frame sees the same list.
  • Next: memoization, for recursion that asks the same question twice, and backtracking, for recursion that makes a choice, explores it and undoes it.
Back to path