W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
在前面的文章中我們提到過可以使用遞歸函數(shù)來消除需要使用 var 變量的 while 循環(huán)。下面為一個使用逼近方法求解的一個遞歸函數(shù)表達:
def approximate(guess: Double) : Double =
if (isGoodEnough(guess)) guess
else approximate(improve(guess))
通過實現(xiàn)合適的 isGoodEnough 和 improve 函數(shù),說明這段代碼在搜索問題中經(jīng)常使用。 如果你打算 approximate 運行的快些,你很可能使用下面循環(huán)來實現(xiàn)什么的算法:
def approximateLoop(initialGuess: Double) : Double = {
var guess = initialGuess
while(!isGoodEnough(guess))
guess=improve(guess)
guess
}
那么這兩種實現(xiàn)哪一種更可取呢? 從簡潔度和避免使用 var 變量上看,使用函數(shù)化編程遞歸比較好。但是有 whil e循環(huán)是否運行效率更高些?實際上,如果我們通過測試,兩種方法所需時間幾乎相同,這聽起來有些不可思議,因為回調(diào)函數(shù)看起來比使用循環(huán)要耗時得多。
其實,對于 approximate 的遞歸實現(xiàn),Scala 編譯器會做些優(yōu)化,我們可以看到 approximate 的實現(xiàn),最后一行還是調(diào)用 approximate 本身,我們把這種遞歸叫做尾遞歸。Scala 編譯器可以檢測到尾遞歸而使用循環(huán)來代替。因此,你應該習慣使用遞歸函數(shù)來解決問題,如果是尾遞歸,那么在效率時不會有什么損失。
一個尾遞歸函數(shù)在每次調(diào)用不會構(gòu)造一個新的調(diào)用棧(stack frame)。所有遞歸都在同一個執(zhí)行棧中運行,如果你在調(diào)試時,如果在尾遞歸中調(diào)試錯誤,不會看到嵌套的調(diào)用棧,比如下面的代碼,非尾遞歸的一個實現(xiàn):
scala> def boom(x:Int):Int=
| if(x==0) throw new Exception("boom!") else boom(x-1) + 1
boom: (x: Int)Int
scala> boom(5)
java.lang.Exception: boom!
at .boom(<console>:8)
at .boom(<console>:8)
at .boom(<console>:8)
at .boom(<console>:8)
at .boom(<console>:8)
at .boom(<console>:8)
... 32 elided
boom 不是一個尾遞歸,因為最后一行為一個遞歸加一操作,可以看到調(diào)用 boom(5)的調(diào)用棧,為多層。
我們修改這段代碼,使它構(gòu)成一個尾遞歸:
scala> def bang(x:Int):Int=
| if(x==0) throw new Exception("boom!") else bang(x-1)
bang: (x: Int)Int
scala> bang(5)
java.lang.Exception: boom!
at .bang(<console>:8)
... 32 elided
這一次,只看到一層調(diào)用棧,Scala 編譯器將尾遞歸優(yōu)化從循環(huán)實現(xiàn),如果你不想使用這個特性,可以添加 scalac 編譯參數(shù) -g:notailcalls 來取消這個優(yōu)化,此后,如果拋出異常,尾遞歸也會顯示多層調(diào)用棧。
目前來說,Scala 編譯器只能對那些直接實現(xiàn)尾遞歸的函數(shù),比如前面的 approximate 和 bang,如果一個函數(shù)間接實現(xiàn)尾遞歸,比如下面代碼:
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)
isEven 和 isOdd 事件也是為遞歸,但不是直接定義的尾遞歸,scala 編譯器無法對這種遞歸進行優(yōu)化。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: