-
Recursion, snakes and tail call optimization
ππππππππππππ
+
πππππππππ
=
β€οΈ
"to understand recursion you first need to understand recursion"
Factorial!
Β Β Β π /jluna
Β Β Β Β π InTheBeach.png.exe
Β Β Β Β Β Β Β π /Downloads
π /home
Β Β Β π passwrd.txt
Β Β Β π /jluna
Β Β Β π /Downloads
Β Β Β Β π example.py
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4]
[3, 4]
Nope, but 4 < 5
Nope, but 4 > 2
Oh yeah!!
Β
Β
def factorial(n):
if n inΒ {0, 1}:
return 1
return n * factorial(n-1) <---
def fib(n):
if n in {0, 1}:
return n
return fib(n-1) + fib(n-2) <-- 2x
###
def a():
b() <---
def b():
a() <---
###
def f(callable):
callable() <---
Β
...
def f9996():
return f9995()
def f9997():
return f9996()
def f9998():
return f9997()
def f9999():
return f9998()
f9999()
π
Β File "recursive.py", line 18004, in f9001
Β Β return f9000()
RecursionError: maximum recursion depth exceeded
f_a(1,2)
f_b()
f_c(42)
f_d("hello!")
__main__()
def fun_a():
return "Hello!"
def fun_b():
return fun_a()
fun_b() <---
__main__() -> fun_b()
__main__
def fun_a():
return "Hello!"
def fun_b(): <---
return fun_a() <---
fun_b()
__main__()
Β Β Β Β βββfun_b() -> fun_a()
Β
fun_b()
__main__
def fun_a(): <---
return "Hello!" <---
def fun_b():
return fun_a()
fun_b()
__main__()
Β Β Β Β βββfun_b()
Β Β Β Β Β Β Β Β Β Β Β βββ fun_a() -> "Hello!"
Β
fun_b()
fun_a()
__main__
def fun_a():
return "Hello!"
def fun_b():
return fun_a() <--- "Hello!"
fun_b()
__main__()
Β Β Β Β βββfun_b() -> "Hello!"
fun_b()
__main__
def fun_a():
return "Hello!"
def fun_b():
return fun_a()
fun_b() <--- "Hello!"
__main__() -> "Hello!"
def f_a(arg1, arg2):
local_var = 3
return f_b(3, 4)
Loaded variables:
arg1, arg2, local_var, f_b
Code itself
Book-keeping:
file name, row #, reference to past stack frames..
function(3, 4)
import inspect
stack = inspect.stack()
current_frame = stack[0]
loaded_vars = current_frame.frame.f_locals
print(loaded_vars)
f_a(1,2)
f_b()
f_c(42)
f_d("hello!")
Logical limit
Physical limit
f_a(1,2)
f_b()
f_c(42)
f_d("hello!")
The value of the logical limit depends on the system, but is usually capped to 1000
import sys
sys.getrecursionlimit() -> 1000
sys.setrecursionlimit(100_000_000)
f_a(1,2)
f_b()
f_c(42)
f_d("hello!")
Logical limit
Physical limit
f_d("hello!")
(or...not *cries recursively*)
A call to a subroutine is "in the tail" if it is the last statement of a procedure
def f():
return a()
def f2(n):
if n == 1:
return a()
else:
return b()
def f():
result = a()
print(result)
return result
def f2(n):
return a() + 1
β
β
f_a(1,2)
f_b()
f_c(42)
f_d("hello!")
a()
ββb() -> c()
a()
b()
a()
ββ c()
a()
c()
a()
a() "wtf I called b() not c()"
Β
Traceback (most recent call last):
File "illooo.py", line 10, in <module>
c()
File "illooo.py", line 8, in c
b()
File "illooo.py", line 5, in b
a()
File "illooo.py", line 2, in a
raise Exception("Obscure exception")
Exception: Obscure exception
Traceback (most recent call last):
File "illooo.py", line 5, in <module>
a()
File "illooo.py", line 2, in a
raise Exception("Obscure exception")
Exception: Obscure exception
Con TCO
Without TCO
def fib(n):
if n in {0, 1}:
return n
return fib(n-1) + fib(n-2)
def fib(n):
if n in {0, 1}:
return n
for _ in range(n):
a, b = b, a + b
return a
πππππππππππππ
π