A function that calls itself
Recursion is when a function solves a problem by calling itself on a smaller version of the same problem. It is the natural way to write many algorithms, including merge sort, and it is the key to the two lessons that follow.
Think of a folder of documents that holds more folders. To count every file, you count the files in this folder, then do the same job in each folder inside it.
By the end of this lesson
Note
I can write a recursive function with a base case.
Key words
| Word | What it means | Example |
|---|---|---|
| Recursion | a function that calls itself | |
| Base case | the input where it stops and answers directly | |
| Recursive case | the input where it calls itself on a smaller problem | |
| Call stack | the pile of calls that are still waiting | |
| Stack frame | one call on the pile, with its own values | |
| Factorial | n times every whole number below it down to 1 | 4! = 24 |
The cells students fill in are left blank in this preview.
Every recursion needs a base case
A recursive function has two parts. The base case is an input so small that the answer is known, so the function returns it without calling itself.
The recursive case calls the function on a smaller input and uses the answer. The factorial of 5 is 5 times the factorial of 4, and the factorial of 1 is 1.
Run a recursive factorial
# A recursive function calls itself on a smaller problem.
def factorial(n):
if n <= 1:
# Base case: stop here.
return 1
# Recursive case: n times the factorial of n - 1.
return n * factorial(n - 1)
print(factorial(5))
# Change 5 to 10 and run again.
Worked example: factorial(4) call by call
| Call | It waits for | It returns |
|---|---|---|
| factorial(4) | 4 times factorial(3) | 24 |
| factorial(3) | 3 times factorial(2) | 6 |
| factorial(2) | 2 times factorial(1) | 2 |
| factorial(1) | Nothing: the base case | 1 |
The cells students fill in are left blank in this preview.
Check a factorial with Python
python3 -c "import math; print(math.factorial(10))"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
The call stack holds the waiting calls
When a function calls itself, the first call has not finished. It waits. Python keeps each waiting call in a stack frame on the call stack, a pile where the newest call is on top.
In the table above, four frames are on the pile at the deepest point. Then the top one returns, and the pile shrinks one frame at a time.
Python stops a recursion that goes too deep
Each frame uses memory, so a stack cannot grow for ever. Python allows about 1000 frames by default, then raises an error called RecursionError.
A function with no base case never stops calling itself. The first Run asks for the limit. The second Run starts an endless recursion and shows the last line of the error.
Ask Python for its recursion limit
python3 -c "import sys; print(sys.getrecursionlimit())"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Start a recursion that never stops
python3 -c "exec('def f(n):\n return f(n + 1)\nf(0)')" 2>&1 | tail -n 1Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A recursion can repeat the same work
The Fibonacci numbers start 0, 1, and each next one is the sum of the two before: 0, 1, 1, 2, 3, 5, 8. So fib(n) is fib(n - 1) plus fib(n - 2), and fib(0) and fib(1) are the base cases.
This function calls itself twice, so the number of calls roughly doubles each time n goes up by one.
Run Fibonacci and count the calls
# Fibonacci by recursion, counting how many calls it makes.
calls = [0]
def fib(n):
calls[0] = calls[0] + 1
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print("fib(10) =", fib(10), "calls", calls[0])
# Change 10 to 15 and run again. Watch the calls.
Worked example: calls made by fib(n)
| n | fib(n) | Calls |
|---|---|---|
| 5 | 5 | 15 |
| 10 | 55 | 177 |
| 20 | 6,765 | 21,891 |
| 30 | 832,040 | 2,692,537 |
The cells students fill in are left blank in this preview.
Write the same function to a file
printf 'def fib(n):\n if n < 2:\n return n\n return fib(n - 1) + fib(n - 2)\n\nprint(fib(30))\n' > fib.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run it for n = 30
python3 fib.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people think a recursive function stops by itself when the numbers get small. Nothing stops it unless you write a base case that returns without calling again. Always write the base case first, then check that every call moves closer to it.