今天開始學習Kotlin的第二大思想,函數式編程。
函數式編程在中一切都是函數。
核心概念:
- 函數和其它數據類型是一樣的,可以作爲其它函數的參數傳入,也可做爲函數的返回值。
- 使用表達式,不用語句。
- 高階函數:一個函數可做爲另一個函數的參數和返回值
- 無副作用:函數執行過程會返回一個結果,不會修改外部變量
高階函數
函數式編程的關鍵是支持高階函數,就是說一個函數可以做爲另一個函數參數或者返回值。
函數類型
先看一下什麼是函數類型,在Kotlin中每個函數都有一個類型,稱爲函數類型,函數類型是中數據類型
fun main(args: Array<String>) {
var a: (Int, Int) -> Int = ::add
println(a(1, 2))
}
fun add(a: Int, b: Int): Int {
return a + b
}
第三行中的 (Int, Int) -> Int 就是函數類型,意思聲明一個屬性類型爲函數類型。
(Int, Int) 說明這個函數要滿足,兩個Int參數 -> Int 是指返回值是Int類型。
(參數:參數類型)->返回值類型 參數可以有多個,也可以沒有就省略。
fun main(args: Array<String>) {
var a: () -> Unit = ::test
println(a())
}
fun test() {
println("無返回值的函數")
}
這是無返回值的函數,如果,不學返回值默認爲無返回值。
函數字面量
函數類型可以聲明的變量就是函數字面量
函數字面量可以接收三種類型的數據類型。
- 函數的引用
- 匿名函數
- Lambda表達式
fun main(args: Array<String>) {
var a = ::add //函數的引用
var b = ::sub //函數的引用
println(a(1, 1))
println(b(2, 1))
println(ride(1, 10)) //匿名函數
println(divide(10, 1)) //Lambda表達式
}
fun add(a: Int, b: Int): Int {
return a + b
}
fun sub(a: Int, b: Int): Int {
return a - b
}
var ride = fun(a: Int, b: Int): Int {
return a * b
}
var divide = { a: Int, b: Int -> a / b }
函數返回值
把一個函數做另一個函數的返回值使用,這個函數就是高階函數。
fun main(args: Array<String>) {
val getfun = getfun("add")
println(getfun(1, 2))
}
fun getfun(string: String): (Int, Int) -> Int {
var a: (Int, Int) -> Int
when (string) {
"add" -> a = ::add
else -> {
a = ::sum
}
}
return a
}
fun add(a: Int, b: Int): Int {
return a + b
}
fun sum(a: Int, b: Int): Int {
return a + b
}
函數參數
fun main(args: Array<String>) {
setFun(add(1, 2))
}
fun setFun(funName: (Int, Int) -> Int) {
println(funName)
}
fun add(a: Int, b: Int): (Int, Int) -> Int {
return fun(a: Int, b: Int): Int {
return a + b
}
}
Lambda表達式
Lambda表達式是一種匿名函數,它可以做表達式、函數、參數和返回值用
剛纔的案例可以改爲Lambda表達式用
fun main(args: Array<String>) {
setFun({ a, b -> a + b })
}
fun setFun(funName: (Int, Int) -> Int) {
println(funName)
}
fun add(a: Int, b: Int): (Int, Int) -> Int {
return { a, b -> a + b }
}
Lambda 怎麼寫
{參數列表:參數類型->Lambda體}
或者在簡寫{參數列表->Lambda體}
不寫參數類型,因爲有時候可以自動推到類型Lambda
尾隨Lambda
fun main(args: Array<String>) {
setFun() { a, b -> a + b }
}
fun setFun(funName: (Int, Int) -> Int) {
println(funName(1, 2))
}
如果函數最後一個是Lambda的時候就可以放到小括號外面
省略參數聲明
fun main(args: Array<String>) {
getString("abc", { it.reversed() })
}
fun getString(s: String, funName: (String) -> String) {
println(funName(s))
}
Lambda中的return語句
在Lambda直接使用return會使函數直接退出,而不是退出Lambda表達式。
fun main(args: Array<String>) {
getString("aaAbbb")
}
fun getString(s: String) {
s.forEach {
println(it)
if (it == 'A') {
return
}
println("Lambda")
}
println("函數退出")
}
沒有打印出 函數退出,證明它直接結束了函數,而非Lambda表達式。
我們用之前學的知識改一下
fun main(args: Array<String>) {
getString("aaAbbb")
}
fun getString(s: String) {
forEach@ s.forEach {
println(it)
if (it == 'A') {
return@forEach
}
println("Lambda")
}
println("函數退出")
}
結果:
這次看到了,說明是結束的Lambda表達式
閉包與捕獲變量
閉包使一種特殊的函數,它可以訪問函數體之外的變量
fun main(args: Array<String>) {
val test = test()
println(test(10))
println(test(10))
println(test(10))
}
fun test(): (Int) -> Int {
var vaule = 10
fun add(a: Int): Int {
vaule += a
return vaule
}
return ::add
}
結果:
可以看到,結果是累加了,這就是閉包捕獲變量。這些變量會被保存到一個特殊的容器中,即使超過作用域在閉包體中也可以訪問到。
內聯函數
待補充