2010-07-10 34 views
7

剛剛看到一個有趣的可能性初始化代碼塊Scala中高階功能,如的foreach或地圖:階的foreach和地圖初始化

(1 to 3) map { 
    val t = 5 
    i => i * 5 
} 


(1 to 3) foreach { 
    val line = Console.readLine 
    i => println(line) 
} 

這是一些記錄功能,或者我應該避免這樣的構造?我可以想象,「初始化」塊進入構造函數,閉包本身變成一個apply()方法?

感謝帕特爲原來的問題(http://extrabright.com/blog/2010/07/10/scala-question-regarding-readline

回答

12

雖然使用的功能並不少見,但我承認這是一個相當奇特的功能組合。基本技巧是Scala中的任何塊都是一個表達式,類型與塊中的最後一個表達式相同。如果最後一個表達式是一個函數,這意味着該塊具有功能類型,因此可以用作「映射」或「foreach」的參數。在這些情況下會發生什麼情況是,當調用「map」或「foreach」時,將對塊進行評估。該塊評估爲一個函數(第一種情況下i => i * 5),然後將該函數映射到範圍內。

該構造的一個可能用途是塊定義可變變量,並且每次調用變量時,結果函數都會變量變量。變量將被初始化一次,被函數關閉,並且每次函數被調用時它們的值都會更新。

例如,這裏的計算第一6個階乘號碼

(1 to 6) map { 
     var total = 1 
     i => {total *= i;total} 
    } 

(順便說一句,對不起使用階乘作爲一個例子的一個有點令人驚訝的方式,這是要麼或斐波納契。功能 預設電臺協會規則。你得面對這個問題,在大廳裏把它和男孩一起拿下來。)

有一個塊返回一個函數的一個不太必要的理由是在塊的前面定義輔助函數。舉例來說,如果你的第二個例子是不是

(1 to 3) foreach { 
    def line = Console.readLine 
    i => println(line) 
} 

的結果將是三條線中讀取和呼應各一次,而你的例子有行讀取一次,呼應三次。

+0

比我的答案要精確得多。 +1 – VonC 2010-07-10 21:10:28

+0

在因子的例子中,你應該使用'total * = i'而不是引入第二個變量'counter' – 2010-07-11 20:47:01

+0

是的,我後來才意識到。將編輯 – 2010-07-12 02:09:34

1

首先,原來的博客「Scala Question Regarding readLine」後提

的評論的「line」是一個值,不能被執行,這是從「Console.readLine」方法執行的結果中僅分配一次。
它在關閉時使用少於三次。
但是,如果你把它定義爲一種方法,它會被執行三次:

(1 to 3) foreach { 
    def line = Console.readLine 
    i => println(line) 
} 

的博客Scala for Java Refugees Part 6: Getting Over Java對高階函數一個有趣的部分,包括:

斯卡拉提供仍這些高階函數的語法更靈活。
在迭代調用中,我們正在創建一個完整的匿名方法,以便再次調用println(String)方法。
考慮到println(String)本身是一種需要String並返回Unit的方法,人們會認爲我們可以壓縮這一點。事實證明,我們可以:

iterate(a, println) 

由於省略了括號,只是指定方法的名稱,我們告訴我們要使用println作爲功能值,Scala編譯器,通過它到iterate方法。
因此,我們不是創建一個新的方法來處理一組調用,而是傳入一個已經做到了我們想要的舊方法。
這是C和C++中常見的模式。實際上,將函數作爲函數值傳遞的語法完全相同。似乎有些東西永遠不會改變...