我定义了一个名为 fab
的阶乘函数。我使用生成器来避免堆栈溢出。但是当我尝试编写装饰器版本时,出现了一些我无法理解的事情,它更直观:
import types
def TCO(f):
def inner(*nkw,**kw):
gen=f(*nkw,**kw)
while isinstance(gen,types.GeneratorType):
gen=gen.next()
return gen
return inner
def fab(n,s=1):
if n<2:
yield s
else:
yield fab(n-1,s*n)
x=TCO(fab)
print x(2500) #this works fine, overcoming the limitation of tail-recursion.
fab=TCO(fab) #now just change the variable name.
print fab(5) #this woks fine.
print fab(2500) #this will raise an error :maximum recursion limit exceeded
为什么?我知道它与同名fab
有关,但为什么fab(5)
工作正常?我认为当我定义fab=TCO(fab)
时,我实际上将f
引用的对象更改为对象TCO(fab)
inner
。所以当 fab(5) 运行时, gen
永远不会是生成器!因为inner
永远不会返回生成器!
我疯了...为什么?
fab=TCO(fab)
语句现在使变量fab
指向函数inner
。在那之后,当
yield fab(n-1,s*n)
遇到这种情况,它实际上并不是调用原始fab
函数,而是调用inner
函数,而函数又调用原始fab
函数。基本上,您每次都会从生成器中出来并输入另一个功能。这就是您收到错误maximum recursion limit exceeded
的原因。
我认为这将是一个很好的例子来解释发生的事情:
def f(x):
print 'original'
if x > 0:
return f(x-1)
return 0
g = f
def f(x):
print 'new'
return x
print g(5)
结果:
original
new
4
这证明:
1.when g(5) runs, origninal `f` is called, not new `f`.
2.as 5>0, 'return f(x-1)' is executed
3.new `f` is called when `f(x-1)` runs. so the result is 4.
您的原始版本产生一个生成器,该生成器产生一个生成器,该生成器产生一个生成器,依此类推。 要评估该链表,您可以使用装饰器中的while
循环TCO
。 这一切都是基于您链接生成器以避免大型堆栈的事实。
当您为变量分配新内容时,这个概念被打破了 fab
. 然后yield
fab(n-1, s*n)
fab
访问新的fab
变量,因此不再返回生成器,而是返回一个值。 要计算该值,将使用堆栈,因此可能会出现堆栈溢出问题。