I define a Factorial function named fab
. I use generator to avoid stack overflow.But something I can't understand came up when I try to write a decorator version, which is more intuitive :
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
Why? I know it has something to do with the same name fab
, but why fab(5)
works fine? I think when I define fab=TCO(fab)
, I actually change the object refered by f
in inner
to object TCO(fab)
. So when fab(5) runs, the gen
will never be a generator! Because the inner
never returns generator!
I am mad...Why ?
fab=TCO(fab)
statment now makes the variable fab
points to the function inner
. After that, when
yield fab(n-1,s*n)
is encountered, it is not actually calling the original fab
function but the inner
function which in turn calls the original fab
function. Basically, you are coming out of the generator and entering another function every time. Thats why you are getting maximum recursion limit exceeded
error.
I think this would be a good example to explain what happens:
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)
result:
original
new
4
this proves:
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.
Your original version yields a generator which yields a generator which yields a generator, etc. To evaluate that linked list you then use the while
loop in you TCO
decorator. It all is based on the fact that you are linking generators to avoid a large stack.
This concept is broken when you assign something new to the variable fab
. Then yield
ing fab(n-1, s*n)
in fab
is accessing the new fab
variable and thus not returning a generator anymore but a value. To compute that value, the stack is used, hence you can get the stack overflow problem.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.