2013-11-04 25 views
2

我定義了一個名爲fab的因子函數。我用的發電機,以避免棧overflow.But的東西我不明白過來時,我嘗試寫一個裝飾的版本,這是更直觀:當我將變量'x'更改爲'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)時,我實際上將inner中的f引用的對象更改爲對象TCO(fab)。所以當晶圓廠(5)運行時,gen永遠不會是一個發電機!因爲inner永遠不會返回發電機!

我瘋了......爲什麼?

+0

能產生恰好一次應該是一臺發電機一個函數。 – Alfe

+0

@Alfe情況是不同的,如果我使用'return',它與python中的普通尾遞歸形式相同並且遭受遞歸限制。 – tcpiper

+0

您可以返回一個lambda以避免立即評估。我覺得在這裏使用一臺發電機並沒有清理乾淨,而是隱藏了真正的意圖。 – Alfe

回答

3
fab=TCO(fab) 

陳述現在使變量fab指向功能inner。在此之後,當遇到

yield fab(n-1,s*n) 

,它不是實際調用原fab功能,但inner功能依次調用原fab功能。基本上,你每次都會從發生器出來並進入另一個功能。這就是爲什麼你得到maximum recursion limit exceeded錯誤。

+0

這是否意味着'inner'中的'f'仍然指原始的'fab',但是當代碼被執行時,原始'fab'中的chars'fab是指'inner''? – tcpiper

+0

我忘了@ = =。請向上看 – tcpiper

+0

@Pythoner是的,你是對的。 '內部'中的'f'仍然指原來的'fab',但當我們重新分配它時,'fab'指向'inner'。 – thefourtheye

0

您的原始版本會生成一個生成器,該生成器會生成一個生成器,生成一個生成器等。要評估該鏈接列表,您可以使用while循環中的裝飾器TCO。這一切都基於這樣的事實,即您鏈接發電機以避免大型堆棧。

當您爲變量fab分配新的內容時,此概念被破壞。然後yield ing fab(n-1, s*n)fab正在訪問新的fab變量,因此不再返回一個生成器,而是返回一個值。爲了計算這個值,使用堆棧,因此你可以得到堆棧溢出的問題。

1

我認爲這將是一個很好的例子來解釋發生了什麼:

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. 
相關問題