2015-07-12 41 views
7

我正在通過Coursera課程(progfun)學習Scala。Scala使用可變變量來實現其apis

我們正在學習如何在功能上進行思考,並在可能的情況下使用尾遞歸來實現函數/方法。

而且就一個列表功能的foreach一個例子,我們教實現它,如:

def foreach[T](list: List[T], f: [T] => Unit) { 
    if (!list.isEmpty) foreach(list.tail) else f(list.head) 
} 

然後,我很驚訝,當我發現在一些斯卡拉以下實現的API:

override /*IterableLike*/ 
    def foreach[B](f: A => B) { 
    var these = this 
    while (!these.isEmpty) { 
     f(these.head) 
     these = these.tail 
    } 
} 

那麼我們如何才能學會使用遞歸併避免使用可變變量,並且通過相反的技術來實現api?

看看scala.collection.LinearSeqOptimized其中scala.collection.immutable.List擴展。 (類似的實現在List類本身中找到)

+2

遞歸容易使某些算法更簡單,更優雅,但運行速度較慢,佔用更多的堆棧如果不是尾部優化,需要一定的思維過程,開發這可能是困難一些。許多自然過程是遞歸的,但直到我們也有類似的天然電腦沒有幫助。爭取更多的觀點對「遞歸的缺點」搜索。見http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html的吉多·範羅蘇姆的觀點尤其是第三個。 – 2015-07-12 14:34:37

回答

4

不要忘記,Scala旨在成爲一種多種語言。爲了教育目的,知道如何讀寫tail-call遞歸函數是很好的。但是,每天使用該語言時,請務必記住它不是純粹的FP。

圖書館的一部分可能會先於TCO和@tailrec註釋。你必須查看提交歷史才能找到答案。

foreach的實現可能使用可變var,但從外部看,它似乎是純粹的。最終,這正是TCO在幕後做的事情。

4

有兩個部分你的問題:

那麼爲什麼我們正在學會用遞歸和避免使用可變的變量

因爲老師認爲你不是已經知道勢在必行無論如何,在你職業生涯中的某個時候,你可能會接觸到它,所以他們寧願專注於教你自己不太可能接受的事情。

此外,可變狀態的命令式編程更難以推理,難以理解,因此難以教導。

和API是通過相反的技術實現?

由於Scala標準庫旨在成爲一個高性能的工業級庫,而不是一個教學示例。也許編寫代碼的人對它進行了剖析並測量出它比尾遞歸版本快0.001%。也許,當編寫代碼時,編譯器還不能可靠地優化尾遞歸版本。

不要忘記,Iterable和朋友是Scala的集合庫的基石,您正在查看的那些方法可能是整個Scala Universe中最常執行的方法。即使是最微小的性能優化,也可以通過執行數十億次的方法實現。