將函數賦值給變數 // Scala中的函數是一等公民,可以獨立定義,獨立存在,而且可以直接將函數作為值賦值給變數 // Scala的語法規定,將函數賦值給變數時,必須在函數後面加上空格和下劃線 def sayHello(name: String) { println("Hello, " + name ...
將函數賦值給變數
// Scala中的函數是一等公民,可以獨立定義,獨立存在,而且可以直接將函數作為值賦值給變數
// Scala的語法規定,將函數賦值給變數時,必須在函數後面加上空格和下劃線
def sayHello(name: String) { println("Hello, " + name) }
val sayHelloFunc = sayHello _
sayHelloFunc("leo")
匿名函數
// Scala中,函數也可以不需要命名,此時函數被稱為匿名函數。// 可以直接定義函數之後,將函數賦值給某個變數;也可以將直接定義的匿名函數傳入其他函數之中
// Scala定義匿名函數的語法規則就是,(參數名: 參數類型) => 函數體
// 這種匿名函數的語法必須深刻理解和掌握,在spark的中有大量這樣的語法,如果沒有掌握,是看不懂spark源碼的
val sayHelloFunc = (name: String) => println("Hello, " + name)
高階函數
// Scala中,由於函數是一等公民,因此可以直接將某個函數傳入其他函數,作為參數。這個功能是極其強大的,也是Java這種面向對象的編程語言所不具備的。
// 接收其他函數作為參數的函數,也被稱作高階函數(higher-order function)
val sayHelloFunc = (name: String) => println("Hello, " + name)
def greeting(func: (String) => Unit, name: String) { func(name) }
greeting(sayHelloFunc, "leo")
Array(1, 2, 3, 4, 5).map((num: Int) => num * num)
// 高階函數的另外一個功能是將函數作為返回值
def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name)
val greetingFunc = getGreetingFunc("hello")
greetingFunc("leo")
高階函數的類型推斷
// 高階函數可以自動推斷出參數類型,而不需要寫明類型;而且對於只有一個參數的函數,還可以省去其小括弧;如果僅有的一個參數在右側的函數體內只使用一次,則還可以將接收參數省略,並且將參數用_來替代
// 諸如3 * _的這種語法,必須掌握!!spark源碼中大量使用了這種語法!
def greeting(func: (String) => Unit, name: String) { func(name) }
greeting((name: String) => println("Hello, " + name), "leo")
greeting((name) => println("Hello, " + name), "leo")
greeting(name => println("Hello, " + name), "leo")
def triple(func: (Int) => Int) = { func(3) }
triple(3 * _)
Scala的常用高階函數
// map: 對傳入的每個元素都進行映射,返回一個處理後的元素
Array(1, 2, 3, 4, 5).map(2 * _)
// foreach: 對傳入的每個元素都進行處理,但是沒有返回值
(1 to 9).map("*" * _).foreach(println _)
// filter: 對傳入的每個元素都進行條件判斷,如果對元素返回true,則保留該元素,否則過濾掉該元素
(1 to 20).filter(_ % 2 == 0)
// reduceLeft: 從左側元素開始,進行reduce操作,即先對元素1和元素2進行處理,然後將結果與元素3處理,再將結果與元素4處理,依次類推,即為reduce;reduce操作必須掌握!spark編程的重點!!!
// 下麵這個操作就相當於1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9
(1 to 9).reduceLeft( _ * _)
// sortWith: 對元素進行兩兩相比,進行排序
Array(3, 2, 5, 4, 10, 1).sortWith(_ < _)
閉包
// 閉包最簡潔的解釋:函數在變數不處於其有效作用域時,還能夠對變數進行訪問,即為閉包
def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name)
val greetingFuncHello = getGreetingFunc("hello")
val greetingFuncHi = getGreetingFunc("hi")
// 兩次調用getGreetingFunc函數,傳入不同的msg,並創建不同的函數返回
// 然而,msg只是一個局部變數,卻在getGreetingFunc執行完之後,還可以繼續存在創建的函數之中;greetingFuncHello("leo"),調用時,值為"hello"的msg被保留在了函數體內部,可以反覆的使用
// 這種變數超出了其作用域,還可以使用的情況,即為閉包
// Scala通過為每個函數創建對象來實現閉包,實際上對於getGreetingFunc函數創建的函數,msg是作為函數對象的變數存在的,因此每個函數才可以擁有不同的msg
// Scala編譯器會確保上述閉包機制
SAM轉換
// 在Java中,不支持直接將函數傳入一個方法作為參數,通常來說,唯一的辦法就是定義一個實現了某個介面的類的實例對象,該對象只有一個方法;而這些介面都只有單個的抽象方法,也就是single abstract method,簡稱為SAM
// 由於Scala是可以調用Java的代碼的,因此當我們調用Java的某個方法時,可能就不得不創建SAM傳遞給方法,非常麻煩;但是Scala又是支持直接傳遞函數的。此時就可以使用Scala提供的,在調用Java方法時,使用的功能,SAM轉換,即將SAM轉換為Scala函數
// 要使用SAM轉換,需要使用Scala提供的特性,隱式轉換
import javax.swing._
import java.awt.event._
val button = new JButton("Click")
button.addActionListener(new ActionListener {
override def actionPerformed(event: ActionEvent) {
println("Click Me!!!")
}
})
implicit def getActionListener(actionProcessFunc: (ActionEvent) => Unit) = new ActionListener {
override def actionPerformed(event: ActionEvent) {
actionProcessFunc(event)
}
}
button.addActionListener((event: ActionEvent) => println("Click Me!!!"))
Currying函數
// Curring函數,指的是,將原來接收兩個參數的一個函數,轉換為兩個函數,第一個函數接收原先的第一個參數,然後返回接收原先第二個參數的第二個函數。
// 在函數調用的過程中,就變為了兩個函數連續調用的形式
// 在Spark的源碼中,也有體現,所以對()()這種形式的Curring函數,必須掌握!
def sum(a: Int, b: Int) = a + b
sum(1, 1)
def sum2(a: Int) = (b: Int) => a + b
sum2(1)(1)
def sum3(a: Int)(b: Int) = a + b
Return
// Scala中,不需要使用return來返回函數的值,函數最後一行語句的值,就是函數的返回值。在Scala中,return用於在匿名函數中返回值給包含匿名函數的帶名函數,並作為帶名函數的返回值。
// 使用return的匿名函數,是必須給出返回類型的,否則無法通過編譯
def greeting(name: String) = {
def sayHello(name: String):String = {
return "Hello, " + name
}
sayHello(name)
}