Tail Call Recursion
The reason Python has a stack of function calls is to deal with the case of a function that calls another function and then does something with the result before returning it:
def fact(n):
"""The factorial function."""
if n == 0:
return 1
else:
return n * fact(n - 1)
Computing fact(4)
looks like this:
fact(4)
= 4 * fact(3)
= 4 * (3 * fact(2))
= 4 * (3 * (2 * fact(1)))
= 4 * (3 * (2 * (1 * fact(0))))
= 4 * (3 * (2 * (1 * 1)))
= 4 * (3 * (2 * 1))
= 4 * (3 * 2)
= 4 * 6
= 24
Python has to store a lot of information for each of those parentheses so it knows to go back to the right place after evaluating each call.
Now let's look at a different function that does the same thing:
def fact2(n, curr_value=1):
if n == 0:
return curr_value
else:
return fact2(n - 1, curr_value * n)
Now evaluating this looks a little different:
fact2(4, 1)
= fact2(3, 4)
= fact2(2, 12)
= fact2(1, 24)
= fact2(0, 24)
= 24
There's no need to remember what the previous function calls were: at each step, we have all of the information we need to get the answer.
This structure is possible whenever the only return
statements your function has are just a single expression. It's called tail-call recursion, because all of the recursion happens at the tail of the function.
Tail Call Recursion
The reason Python has a stack of function calls is to deal with the case of a function that calls another function and then does something with the result before returning it:
def fact(n):
"""The factorial function."""
if n == 0:
return 1
else:
return n * fact(n - 1)
Computing fact(4)
looks like this:
fact(4)
= 4 * fact(3)
= 4 * (3 * fact(2))
= 4 * (3 * (2 * fact(1)))
= 4 * (3 * (2 * (1 * fact(0))))
= 4 * (3 * (2 * (1 * 1)))
= 4 * (3 * (2 * 1))
= 4 * (3 * 2)
= 4 * 6
= 24
Python has to store a lot of information for each of those parentheses so it knows to go back to the right place after evaluating each call.
Now let's look at a different function that does the same thing:
def fact2(n, curr_value=1):
if n == 0:
return curr_value
else:
return fact2(n - 1, curr_value * n)
Now evaluating this looks a little different:
fact2(4, 1)
= fact2(3, 4)
= fact2(2, 12)
= fact2(1, 24)
= fact2(0, 24)
= 24
There's no need to remember what the previous function calls were: at each step, we have all of the information we need to get the answer.
This structure is possible whenever the only return
statements your function has are just a single expression. It's called tail-call recursion, because all of the recursion happens at the tail of the function.