Scala 中函數與方法的區別很是小,若是函數做爲某個對象的成員,這樣的函數被稱爲方法,不然就是一個正常的函數。java
// 定義方法 def multi1(x:Int) = {x * x} // 定義函數 val multi2 = (x: Int) => {x * x} println(multi1(3)) //輸出 9 println(multi2(3)) //輸出 9
也可使用 def
定義函數:git
def multi3 = (x: Int) => {x * x} println(multi3(3)) //輸出 9
multi2
和 multi3
本質上沒有區別,這是由於函數是一等公民,val multi2 = (x: Int) => {x * x}
這個語句至關因而使用 def
預先定義了函數,以後賦值給變量 multi2
。github
上面咱們說過 multi2
和 multi3
本質上是同樣的,那麼做爲函數它們是什麼類型的?二者的類型實際上都是 Int => Int
,前面一個 Int 表明輸入參數類型,後面一個 Int 表明返回值類型。編程
scala> val multi2 = (x: Int) => {x * x} multi2: Int => Int = $$Lambda$1092/594363215@1dd1a777 scala> def multi3 = (x: Int) => {x * x} multi3: Int => Int // 若是有多個參數,則類型爲:(參數類型,參數類型 ...)=>返回值類型 scala> val multi4 = (x: Int,name: String) => {name + x * x } multi4: (Int, String) => String = $$Lambda$1093/1039732747@2eb4fe7
在 Scala 中函數是一等公民,這意味着不只能夠定義函數並調用它們,還能夠將它們做爲值進行傳遞:閉包
import scala.math.ceil object ScalaApp extends App { // 將函數 ceil 賦值給變量 fun,使用下劃線 (_) 指明是 ceil 函數但不傳遞參數 val fun = ceil _ println(fun(2.3456)) //輸出 3.0 }
在 Scala 中你沒必要給每個函數都命名,如 (x: Int) => 3 * x
就是一個匿名函數:函數
object ScalaApp extends App { // 1.匿名函數 (x: Int) => 3 * x // 2.具名函數 val fun = (x: Int) => 3 * x // 3.直接使用匿名函數 val array01 = Array(1, 2, 3).map((x: Int) => 3 * x) // 4.使用佔位符簡寫匿名函數 val array02 = Array(1, 2, 3).map(_ * 3) // 5.使用具名函數 val array03 = Array(1, 2, 3).map(fun) }
在 Java 中若是你想要傳遞可變長度的參數,須要使用 String ...args
這種形式,Scala 中等效的表達爲 args: String*
。oop
object ScalaApp extends App { def echo(args: String*): Unit = { for (arg <- args) println(arg) } echo("spark","hadoop","flink") } // 輸出 spark hadoop flink
向函數傳遞參數時候能夠指定具體的參數名。大數據
object ScalaApp extends App { def detail(name: String, age: Int): Unit = println(name + ":" + age) // 1.按照參數定義的順序傳入 detail("heibaiying", 12) // 2.傳遞參數的時候指定具體的名稱,則沒必要遵循定義的順序 detail(age = 12, name = "heibaiying") }
在定義函數時,能夠爲參數指定默認值。spa
object ScalaApp extends App { def detail(name: String, age: Int = 88): Unit = println(name + ":" + age) // 若是沒有傳遞 age 值,則使用默認值 detail("heibaiying") detail("heibaiying", 12) }
var more = 10 // addMore 一個閉包函數:由於其捕獲了自由變量 more 從而閉合了該函數字面量 val addMore = (x: Int) => x + more
如上函數 addMore
中有兩個變量 x 和 more:scala
按照定義:在建立函數時,若是須要捕獲自由變量,那麼包含指向被捕獲變量的引用的函數就被稱爲閉包函數。
這裏須要注意的是,閉包捕獲的是變量自己,便是對變量自己的引用,這意味着:
// 聲明 more 變量 scala> var more = 10 more: Int = 10 // more 變量必須已經被聲明,不然下面的語句會報錯 scala> val addMore = (x: Int) => {x + more} addMore: Int => Int = $$Lambda$1076/1844473121@876c4f0 scala> addMore(10) res7: Int = 20 // 注意這裏是給 more 變量賦值,而不是從新聲明 more 變量 scala> more=1000 more: Int = 1000 scala> addMore(10) res8: Int = 1010
自由變量可能隨着程序的改變而改變,從而產生多個副本,可是閉包永遠指向建立時候有效的那個變量副本。
// 第一次聲明 more 變量 scala> var more = 10 more: Int = 10 // 建立閉包函數 scala> val addMore10 = (x: Int) => {x + more} addMore10: Int => Int = $$Lambda$1077/1144251618@1bdaa13c // 調用閉包函數 scala> addMore10(9) res9: Int = 19 // 從新聲明 more 變量 scala> var more = 100 more: Int = 100 // 建立新的閉包函數 scala> val addMore100 = (x: Int) => {x + more} addMore100: Int => Int = $$Lambda$1078/626955849@4d0be2ac // 引用的是從新聲明 more 變量 scala> addMore100(9) res10: Int = 109 // 引用的仍是第一次聲明的 more 變量 scala> addMore10(9) res11: Int = 19 // 對於全局而言 more 仍是 100 scala> more res12: Int = 100
從上面的示例能夠看出從新聲明 more
後,全局的 more
的值是 100,可是對於閉包函數 addMore10
仍是引用的是值爲 10 的 more
,這是由虛擬機來實現的,虛擬機會保證 more
變量在從新聲明後,原來的被捕獲的變量副本繼續在堆上保持存活。
定義函數時候支持傳入函數做爲參數,此時新定義的函數被稱爲高階函數。
object ScalaApp extends App { // 1.定義函數 def square = (x: Int) => { x * x } // 2.定義高階函數: 第一個參數是類型爲 Int => Int 的函數 def multi(fun: Int => Int, x: Int) = { fun(x) * 100 } // 3.傳入具名函數 println(multi(square, 5)) // 輸出 2500 // 4.傳入匿名函數 println(multi(_ * 100, 5)) // 輸出 50000 }
咱們上面定義的函數都只支持一個參數列表,而柯里化函數則支持多個參數列表。柯里化指的是將原來接受兩個參數的函數變成接受一個參數的函數的過程。新的函數以原有第二個參數做爲參數。
object ScalaApp extends App { // 定義柯里化函數 def curriedSum(x: Int)(y: Int) = x + y println(curriedSum(2)(3)) //輸出 5 }
這裏當你調用 curriedSum 時候,其實是連着作了兩次傳統的函數調用,實際執行的柯里化過程以下:
x
的 Int 型參數,返回一個用於第二次調用的函數,假設 x
爲 2,則返回函數 2+y
;y
,並計算並返回值 2+3
的值。想要得到柯里化的中間返回的函數其實也比較簡單:
object ScalaApp extends App { // 定義柯里化函數 def curriedSum(x: Int)(y: Int) = x + y println(curriedSum(2)(3)) //輸出 5 // 獲取傳入值爲 10 返回的中間函數 10 + y val plus: Int => Int = curriedSum(10)_ println(plus(3)) //輸出值 13 }
柯里化支持多個參數列表,多個參數按照從左到右的順序依次執行柯里化操做:
object ScalaApp extends App { // 定義柯里化函數 def curriedSum(x: Int)(y: Int)(z: String) = x + y + z println(curriedSum(2)(3)("name")) // 輸出 5name }
更多大數據系列文章能夠參見 GitHub 開源項目: 大數據入門指南