在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Scala/ 函數(shù)–尾遞歸
包對(duì)象
Ordered Trait
組合和繼承–定義 final 成員
基本數(shù)據(jù)類(lèi)型
Match 表達(dá)式
類(lèi)和對(duì)象 (三)
操作基本數(shù)據(jù)類(lèi)型
for 表達(dá)式
組合和繼承–重載成員函數(shù)和方法
類(lèi)和對(duì)象 (二)
組合和繼承–定義 factory 對(duì)象
組合和繼承–多態(tài)和動(dòng)態(tài)綁定
Trait 的基本概念
if 表達(dá)式
組合和繼承–抽象類(lèi)
函數(shù)–函數(shù)字面量的一些簡(jiǎn)化寫(xiě)法
while 循環(huán)
組合和繼承–使用組合還是繼承?
訪問(wèn)控制修飾符
Trait 示例–Rectangular 對(duì)象
組合和繼承–定義參數(shù)化成員變量
組合和繼承–定義無(wú)參數(shù)方法
類(lèi)和對(duì)象 (一)
函數(shù)–閉包
函數(shù)–類(lèi)成員函數(shù)
Scala 基本數(shù)據(jù)類(lèi)型的實(shí)現(xiàn)方法
try 表達(dá)式處理異常
選擇瘦接口還是胖接口設(shè)計(jì)?
組合和繼承–小結(jié)
創(chuàng)建新的控制結(jié)構(gòu)
使用 import
為訪問(wèn)控制修飾符添加作用域
Scala 的類(lèi)層次關(guān)系
類(lèi)和對(duì)象 (五)
傳名參數(shù)
柯里化函數(shù)
函數(shù)–頭等公民
組合和組合和繼承–定義 heighten 和 widen 函數(shù)
使用 Package–將代碼放入包中
隱含的 import
所有類(lèi)的公共子類(lèi)–底層類(lèi)型
進(jìn)一步 Scala
函數(shù)–局部函數(shù)
引用包中的代碼
組合和繼承–使用 override 修飾符
組合和繼承–實(shí)現(xiàn)類(lèi) Element 的 above,beside 和 toString()方法
類(lèi)和對(duì)象 (四)
函數(shù)–尾遞歸
沒(méi)有“break”和“continue”的日子
組合和繼承–調(diào)用基類(lèi)構(gòu)造函數(shù)
減低代碼重復(fù)
函數(shù)–函數(shù)–可變參數(shù),命名參數(shù),缺省參數(shù)
起步 Scala
組合和繼承–擴(kuò)展類(lèi)
函數(shù)–部分應(yīng)用的函數(shù)
開(kāi)始神奇的 Scala編程之旅
組合和繼承–概述
Trait 用來(lái)實(shí)現(xiàn)可疊加的修改操作

函數(shù)–尾遞歸

在前面的文章中我們提到過(guò)可以使用遞歸函數(shù)來(lái)消除需要使用 var 變量的 while 循環(huán)。下面為一個(gè)使用逼近方法求解的一個(gè)遞歸函數(shù)表達(dá):

def approximate(guess: Double) : Double =
  if (isGoodEnough(guess)) guess
  else approximate(improve(guess))

通過(guò)實(shí)現(xiàn)合適的 isGoodEnough 和 improve 函數(shù),說(shuō)明這段代碼在搜索問(wèn)題中經(jīng)常使用。 如果你打算 approximate 運(yùn)行的快些,你很可能使用下面循環(huán)來(lái)實(shí)現(xiàn)什么的算法:

def approximateLoop(initialGuess: Double) : Double = {
  var guess = initialGuess
  while(!isGoodEnough(guess))
    guess=improve(guess)
  guess
 } 

那么這兩種實(shí)現(xiàn)哪一種更可取呢? 從簡(jiǎn)潔度和避免使用 var 變量上看,使用函數(shù)化編程遞歸比較好。但是有 whil e循環(huán)是否運(yùn)行效率更高些?實(shí)際上,如果我們通過(guò)測(cè)試,兩種方法所需時(shí)間幾乎相同,這聽(tīng)起來(lái)有些不可思議,因?yàn)榛卣{(diào)函數(shù)看起來(lái)比使用循環(huán)要耗時(shí)得多。

其實(shí),對(duì)于 approximate 的遞歸實(shí)現(xiàn),Scala 編譯器會(huì)做些優(yōu)化,我們可以看到 approximate 的實(shí)現(xiàn),最后一行還是調(diào)用 approximate 本身,我們把這種遞歸叫做尾遞歸。Scala 編譯器可以檢測(cè)到尾遞歸而使用循環(huán)來(lái)代替。因此,你應(yīng)該習(xí)慣使用遞歸函數(shù)來(lái)解決問(wèn)題,如果是尾遞歸,那么在效率時(shí)不會(huì)有什么損失。

跟蹤尾遞歸函數(shù)

一個(gè)尾遞歸函數(shù)在每次調(diào)用不會(huì)構(gòu)造一個(gè)新的調(diào)用棧(stack frame)。所有遞歸都在同一個(gè)執(zhí)行棧中運(yùn)行,如果你在調(diào)試時(shí),如果在尾遞歸中調(diào)試錯(cuò)誤,不會(huì)看到嵌套的調(diào)用棧,比如下面的代碼,非尾遞歸的一個(gè)實(shí)現(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 不是一個(gè)尾遞歸,因?yàn)樽詈笠恍袨橐粋€(gè)遞歸加一操作,可以看到調(diào)用 boom(5)的調(diào)用棧,為多層。

我們修改這段代碼,使它構(gòu)成一個(gè)尾遞歸:

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)實(shí)現(xiàn),如果你不想使用這個(gè)特性,可以添加 scalac 編譯參數(shù) -g:notailcalls 來(lái)取消這個(gè)優(yōu)化,此后,如果拋出異常,尾遞歸也會(huì)顯示多層調(diào)用棧。

尾遞歸的一些局限性

目前來(lái)說(shuō),Scala 編譯器只能對(duì)那些直接實(shí)現(xiàn)尾遞歸的函數(shù),比如前面的 approximate 和 bang,如果一個(gè)函數(shù)間接實(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 編譯器無(wú)法對(duì)這種遞歸進(jìn)行優(yōu)化。