以下示例来自Allen Downey撰写的“Think Python”一书。在解释词典中“备忘录”的概念时,他引用了下面的例子。
known = {0:0, 1:1}
def fibonacci(n):
if n in known:
return known[n]
res = fibonacci(n-1) + fibonacci(n-2)
known[n] = res
return res
fibonacci(5)
print known
我希望这段代码返回{0:0, 1:1, 5:5}
,因为我没有看到任何迭代计算1到5之间的每个值的函数。但是我看到的是{0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 5}
,当代码是运行(如书所说),但我无法理解为什么函数计算表达式res = fibonacci(n-1) + fibonacci(n-2)
为n = 2,n = 3和n = 4。
有人可以向我解释一下吗?我无法在书中得到解释。
答案 0 :(得分:3)
尝试将print语句放入代码中以跟踪known
的状态:
def fibonacci(n):
print(n, known)
if n in known:
return known[n]
res = fibonacci(n-1) + fibonacci(n-2)
known[n] = res
return res
fibonacci(5)
print(known)
产量
5 {0: 0, 1: 1}
4 {0: 0, 1: 1}
3 {0: 0, 1: 1}
2 {0: 0, 1: 1}
1 {0: 0, 1: 1}
0 {0: 0, 1: 1}
1 {0: 0, 1: 1, 2: 1}
2 {0: 0, 1: 1, 2: 1, 3: 2}
3 {0: 0, 1: 1, 2: 1, 3: 2, 4: 3}
{0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 5}
第一个(整数)值是n
的值。如您所见,fibonacci(5)
是
然后是fibonacci(4)
,然后是fibonacci(3)
,然后是fibonacci(2)
等等
上。这些调用都是由于Python遇到
res = fibonacci(n-1) + fibonacci(n-2)
并递归调用fibonacci(n-1)
。请记住,Python会评估表达式
从左到右。因此,仅在fibonacci(n-1)
返回后才fibonacci(n-2)
调用。
为了更好地理解您可以使用的递归函数调用的顺序 这个装饰者:
import functools
def trace(f):
"""This decorator shows how the function was called.
Especially useful with recursive functions."""
indent = ' ' * 2
@functools.wraps(f)
def wrapper(*arg, **kw):
arg_str = ', '.join(
['{0!r}'.format(a) for a in arg]
+ ['{0} = {1!r}'.format(key, val) for key, val in kw.items()])
function_call = '{n}({a})'.format(n=f.__name__, a=arg_str)
print("{i}--> {c}".format(
i=indent * (trace.level), c=function_call))
trace.level += 1
try:
result = f(*arg, **kw)
print("{i}<-- {c} returns {r}".format(
i=indent * (trace.level - 1), c=function_call, r=result))
finally:
trace.level -= 1
return result
trace.level = 0
return wrapper
known = {0:0, 1:1}
@trace
def fibonacci(n):
# print(n, known)
if n in known:
return known[n]
res = fibonacci(n-1) + fibonacci(n-2)
known[n] = res
return res
fibonacci(5)
print(known)
产生
--> fibonacci(5)
--> fibonacci(4) # fibonacci(5) calls fibonacci(4)
--> fibonacci(3) # fibonacci(4) calls fibonacci(3)
--> fibonacci(2) # fibonacci(3) calls fibonacci(2)
--> fibonacci(1) # fibonacci(2) calls fibonacci(1)
<-- fibonacci(1) returns 1
--> fibonacci(0) # fibonacci(2) calls fibonacci(0)
<-- fibonacci(0) returns 0
<-- fibonacci(2) returns 1
--> fibonacci(1) # fibonacci(3) calls fibonacci(1)
<-- fibonacci(1) returns 1
<-- fibonacci(3) returns 2
--> fibonacci(2) # fibonacci(4) calls fibonacci(2)
<-- fibonacci(2) returns 1
<-- fibonacci(4) returns 3
--> fibonacci(3) # fibonacci(5) calls fibonacci(3)
<-- fibonacci(3) returns 2
<-- fibonacci(5) returns 5
{0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 5}
您可以通过每个递归调用来自的缩进级别来判断。
答案 1 :(得分:0)
如您所见fibonacci
是一个递归函数,这意味着它在函数内调用自身。
例如,考虑fibonacci(2)
。 2
不在known
字典中,因此执行res = fibonacci(1)+fibonacci(0)
。由于0
和1
已知,因此将其值(0和1)添加到res
,以便res=1
,fibonacci(2) = 1
和{2}也会添加到known
中1}}字典,直到达到5.