python函式閉包和遞迴 函式和閉包之尾遞迴

2021-10-21 04:44:00 字數 1883 閱讀 2008

前面提到過,如果想把更新var的while迴圈轉換成僅使用val這種更函式式的風格的話,有時候你可以使用遞迴。下面的例子是通過不斷改善猜測數字來逼近乙個值的遞迴函式:

var guess = initialguess

while(!isgoodenough(guess)) guess = improve(guess)

guess

事實上你不必刻意迴避使用遞迴演算法去解決問題。遞迴經常是比基於迴圈的更優美和簡明的方案。如果方案是尾遞迴,就無須付出任何執行期開銷。

尾遞迴函式追蹤

尾遞迴函式將不會為每個呼叫製造新的堆疊結構;所有的呼叫將在乙個結構內執行。這可能會讓檢查程式堆疊跟蹤資訊並失敗的程式設計師感到驚奇。例如,這個函式呼叫自身若干次之後丟擲了異常:

def boom(x:int):int = {

if(x == 0) throw new exception("boom!")

else boom(x - 1) + 1

這個函式不是尾遞迴,因為在遞迴呼叫之後執行了遞增操作。如果執行:

boom(3)

將會得到預期的:

如果你現在修改了boom從而讓它變成尾遞迴:

def bang(x:int):int =

if(x == 0) throw new exception("bang!")

else bang(x - 1)

執行:bang(5)

你會得到:

這回,你僅看到了bang的乙個堆疊結構。或許你會認為bang在呼叫自己之前就崩潰了,但事實並非如此。如果你認為你會在看到椎棧跟蹤時被尾呼叫優化搞糊塗,你可以用開關項關掉它:-g:notailcalls

把這個引數傳給scala的shell或者scalac編譯器。定義了這個選項,你就能看到乙個長長的堆疊跟蹤。例如,我們在scala的shell中輸入:

d:\> scala -g:notailcalls

然後輸入:

def bang(x:int):int =

if(x == 0) throw new exception("bang!")

else bang(x - 1)

bang(5)

得到結果:

這就是放棄尾遞迴呼叫優化後的結果。

尾遞迴的侷限

scala裡尾遞迴的使用侷限很大,因為jvm指令集使實現更加先進的尾遞迴形式變得很困難。scala僅優化了直接遞迴呼叫使其返回同乙個函式。如果遞迴是間接的,就像在下面的例子裡兩個互相遞迴的函式,就沒有優化的可能性了:

def iseven(x:int):boolean =

if(x == 0) true else isodd(x - 1)

def isodd(x:int):boolean =

if(x == 0) false else iseven(x - 1)

同樣,如果最後乙個呼叫是乙個函式值你也不能獲得尾呼叫優化。請考慮下列遞迴**的實現:

val fuvalue = nestedfun _

def nestedfun(x:int) {

if(x != 0) {

println(x)

funvalue(x - 1)

funvalue變數指向乙個實質是包裝了nestedfun的呼叫的函式值。當你把這個函式值應用到引數上,它會轉向把nestedfun應用到同乙個引數,並返回結果。因此你或許希望scala編譯器能執行尾呼叫優化,但在這個例子裡做不到。因此,尾呼叫優化限定了方法或巢狀函式必須在最後乙個操作呼叫本身,而不是轉到某個函式值或什麼其他的中間函式的情況。

python遞迴 返回 閉包 函式

函式呼叫函式自身,這種方式稱為遞迴,這種函式稱為遞迴函式 遞迴函式的優點是定義簡單,邏輯清晰,缺點是過深的呼叫會導致棧溢位 遞迴函式使用過程中,需要找到不變的規律和停止遞迴的邊界條件,因為函式自身呼叫自身,函式本身的結構不變,只是每次傳的引數改變啦 遞迴實現過程 擴充套件 利用棧的思想,先找邊界條件...

python匿名函式和閉包函式

一 匿名函式 在定義函式的時候,不想給函式起乙個名字。這個時候就可以用lambda來定義乙個匿名函式。語法 變數名 lambda 引數 表示式 引數 可選,通常以逗號分隔 表示式 不能包含迴圈 return,但是可以包含if.else.返回值就是該表示式的結果。如 建立乙個帶引數的匿名函式 add ...

python函式物件和閉包函式

函式物件 函式物件指的是函式可以被當做 資料 來處理,具體可以分為四個方面的使用,我們如下 函式可以被引用 def add x,y return x y func add func 1,2 3 函式可以作為容器型別的元素 dic dic dic add 1,2 3 函式可以作為引數傳入另外乙個函式 ...