
認識 Lambda/Closure(4)從 Scala 中借鏡
認識 Lambda/Closure(3)Python 對 Lambda/Closure 的支援?<< 前情 在〈認識 Lambda/Closure〉系列先前的文章中,我們使用了 JavaScript 與 Python 來示範 Lambda/Closure 為何,以及如何善用它們。就學習 Lambda/Closure 而言,這是個不錯的開始,因為 JavaScript 與 Python 都是動態語言,在這些語言中不用在意變數的型態。 然而在步入靜態語言的世界時,我們都知道編譯器需要型態訊息,以便在編譯時期檢查出各種可能的型態不符之錯誤。這點很有助益,因為可以在撰寫程式的一開始就捕捉到一些錯誤,降低因錯誤而帶來的成本負擔。不過在討論到程式碼的簡潔度時,靜態語言中冗長的型態宣告常是令人詬病的對象。 先來看看 Scala 中如何定義函式好了: def max(m: Int, n: Int): Int = if(m > n) m else n Scala 是靜態語言,所以必須宣告函式的參數型態為何。在這邊型態宣告似乎沒什麼大問題。嗯…來看看如何宣告匿名函式並指定給變數好了。 val max: (Int, Int) => Int = (m: Int, n: Int) => if(m > n) m else n 喔…看來語法一大垞!你必須聲明 def bubbleSort(arr: Array[Int], order: (Int, Int) => Boolean): Unit { ... val o: Boolean = order(a, b) ... } 上例中, val arr: Array[Int] = Array(2, 5, 1, 7, 8) bubbleSort(arr, (a: Int, b: Int) => a > b) 如果在 Scala 中真的得用這麼冗長的語法,你還會想用 Lambda/Closure 嗎?所幸地是,Scala 的編譯器很聰明,能夠進行型態推斷(Type inference)。它能夠從原始碼前後文推斷出型態資訊,所以實際上在宣告變數或撰寫匿名函式時,有很大的機會是不用宣告型態的。例如,實際上先前的程式碼可以重新撰寫為以下的形式: val arr = Array(2, 5, 1, 7, 8) bubbleSort(arr, (a, b) => a > b) 在上例中,Scala 編譯器從 val arr = Array(2, 5, 1, 7, 8) bubbleSort(arr, (_: Int) > (_: Int)) 或者,甚至是以下最短的語法: val arr = Array(2, 5, 1, 7, 8) bubbleSort(arr, _ > _) 在這邊不打算解釋 Scala 是如何完成這類的魔法,如果有興趣瞭解的話,可以進一步看看 Scala 學習筆記。這邊的重點在於,對於靜態語言來說,類型推斷是很重要的功能。在必須提供型態資訊的場合中,類型推斷可以讓程式碼簡潔易讀,像是在宣告變數或者是撰寫匿名函式的時候。Lambda/Closure 是個表達工具,如果沒有型態推斷,過於冗長的語法只會讓開發者望之卻步,不可能讓他們有意願採用。 在下一步文章中,就會來談談 Java 中的 Lambda/Closure。不過,實際上會先來看個 舊的提案,這有助於我們瞭解 Lambda/Closure 何以會演變為現今 JDK8 中的形態。 |