Scala 【 10 函數式編程 】

函數式編程

將函數賦值給變量

​ Scala 中的函數是一等公民,可以獨立定義,獨立存在,而且可以直接將函數作爲值賦值給變量。

一等公民表現在:

1.可以傳遞、賦值

2.嵌套函數和匿名函數

3.具有高階函數

4.偏應用

5.閉包

​ Scala的語法規定,將函數賦值給變量時,必須在函數後面加上空格和下劃線。

def sayHello(name:String){
    println("Hello, " + name)
}

// 注意這裏是 空格 + 下劃線
val sayHelloFunc = sayHello _

sayHelloFunc("Li")
匿名函數

​ Scala 中,函數也可以不需要命名,此時函數被稱爲匿名函數。

​ 可以直接定義函數之後,將函數賦值給某個變量;也可以將直接定義的匿名函數傳入其他函數之中
​ Scala 定義匿名函數的語法規則就是:

​ (參數名: 參數類型) => 函數體

val sayHelloFunc = (name:String) => println("Hello," + name)
高階函數

​ Scala 中,由於函數是一等公民,因此可以直接將某個函數傳入其他函數,作爲參數。這個功能是極其強大的,也是 Java 這種面向對象的編程語言所不具備的。

​ 接收其他函數作爲參數的函數,也被稱作高階函數(higher-order function)。

// example 1

val sayHelloFunc = (name:String) => println("Hello, " + name)

def greeting(func:(String) => Unit, name:String){
    func(name)
}

greeting(sayHelloFunc,"Li")

// example 2
// Array 中的 map 也是高階函數
Array(1,2,3,4,5).map((num:Int) => num * num)

​ 高階函數的另外一個功能是將函數作爲返回值。

def getGreetingFunc(msg:String) = (name:String) => println(msg + "," + name)
// getGreetingFunc 就是一個高階函數,接收一個 String 類型的 msg,返回一個函數,返回的函數會打印一行字符,返回的函數也需要接收一個 String 類型的 name。


val greetingFunc = getGreetingFunc("Hello")

greetingFunc("Li")
// 輸出:Hello,Li


val greetingFuncHi = getGreetingFunc("Hi")

greetingFuncHi("Li")
// 輸出:Hi,Li

// 對於不同參數的參數可以不同的輸出。
高階函數的類型推斷

​ 高階函數可以自動推斷出參數類型,而不需要寫明類型。

​ 而且對於只有一個參數的函數,還可以省去其小括號。

​ 如果僅有的一個參數在右側的函數體內只使用一次,則還可以將接收參數省略,並且將參數用_來替代。

def greeting (func:(String) => Unit, name:String){
    func(name)
}

greeting((name:String) => println("Hello," + name), "Li")

greeting((name) => println("Hello," + name),"Li")
// 自動推斷類型,所以可以省略 String 。

greeting(name => println("Hello," + name) ,"Li")
// 只有一個參數,所以可以去掉小括號。


def triple(func:(Int) => Int) = {
    func(3)
}
// 這個會傳入一個參數 3,而下面的 3 * _ 中的 _ 就是 3 。

triple(3 * _)

Scala的常用高階函數

​ map: 對傳入的每個元素都進行映射,返回一個處理後的元素。

Array(1,2,3,4,5).map(2 * _)

​ foreach: 對傳入的每個元素都進行處理,但是沒有返回值。

(1 to 9).map("*" * _).foreach(println _)

// 第一個 _ 是 1 to 9。
// foreach 中的 _ 是處理的結果。
// 這個意思是輸出 9 行,第 i 行輸出 i 個 * ,每次輸出佔一行。

​ filter: 對傳入的每個元素都進行條件判斷,如果對元素返回 true,則保留該元素,否則過濾掉該元素。

(1 to 20).filter(_ % 2 == 0)

// 輸出結果:scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

​ reduceLeft: 從左側元素開始,進行 reduce 操作,即先對元素 1 和元素 2 進行處理,然後將結果與元素 3處理,再將結果與元素 4 處理,依次類推,即爲 reduce。

(1 to 4).reduceLeft(_ * _)
// 相當於 1 * 2 * 3 * 4

​ sortWith: 對元素進行兩兩相比,進行排序。

Array(3,2,5,4,10,0).sortWith(_<_)
// 結果: 0 2 3 4 5 10
// Array[Int] = Array(0, 2, 3, 4, 5, 10)

閉包

​ 閉包最簡潔的解釋:函數在變量不處於其有效作用域時,還能夠對變量進行訪問,即爲閉包。

def getGreetingFunc(msg:String) = (name:String) => println(msg + "," + name)

val greetingFuncHello = getGreetingFunc("Hello")

val greetingFuncHi = getGreetingFunc("Hi")

greetingFuncHello("Li")
greetingFuncHi("Li")

​ 兩次調用 getGreetingFunc 函數,傳入不同的 msg,並創建不同的函數返回。
​ 然而,msg 只是一個局部變量,卻在 getGreetingFunc 執行完之後,還可以繼續存在創建的函數之中。

​ greetingFuncHello(“Li”),調用時,值爲 “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!")
    }
})

// 測試 error: type mismatch
 button.addActionListener((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 函數指的是,將原來接收兩個參數的一個函數,轉換爲兩個函數,第一個函數接收原先的第一個參數,然後返回接收原先第二個參數的第二個函數。

​ 在函數調用的過程中,就變爲了兩個函數連續調用的形式。

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)
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章