Kotlin函數(shù)式編程 (2)??Lambda表達(dá)式

  • Lambda 表達(dá)式標(biāo)準(zhǔn)語(yǔ)法格式
  • 使用Lambda 表達(dá)式
  • Lambda 表達(dá)式簡(jiǎn)化寫法
    • ??參數(shù)類型推導(dǎo)簡(jiǎn)化
    • ??使用尾隨 Lambda 表達(dá)式
    • ??省略參數(shù)聲明
  • Lambda 表達(dá)式與 return 語(yǔ)句

??Lambda 表達(dá)式是一種匿名函數(shù),可以作為表達(dá)式函數(shù)參數(shù)函數(shù)返回值 使用。

一、Lambda 表達(dá)式標(biāo)準(zhǔn)語(yǔ)法格式

??Lambda 表達(dá)式的語(yǔ)法很靈活,下面只是它的標(biāo)準(zhǔn)語(yǔ)法格式:

{ 參數(shù)列表 ->
    Lambda 體
}

??Lambda 表達(dá)式的參數(shù)列表與函數(shù)的參數(shù)列表形式類似,但是 Lambda 表達(dá)式參數(shù)列表前后 沒(méi)有小括號(hào)() 。箭頭符號(hào)將參數(shù)列表和 Lambda 體分隔開,Lambda 表達(dá)式不需要聲明返回類型。Lambda 表達(dá)式可以有返回值,如果 沒(méi)有 return 語(yǔ)句,Lambda 體的最后一個(gè)表達(dá)式就是 Lambda 表達(dá)式的返回值;如果 有 return 語(yǔ)句,返回值是 return 語(yǔ)句后面的表達(dá)式。

fun calculate(opr: Char): (Int, Int) -> Int = when (opr) {
    '+' -> { a: Int, b: Int -> a + b } // Lambda表達(dá)式
    '-' -> { a: Int, b: Int -> a - b } // Lambda表達(dá)式
    '*' -> { a: Int, b: Int -> a * b } // Lambda表達(dá)式
    else -> { a: Int, b: Int -> a / b } // Lambda表達(dá)式
}

fun main(args: Array<String>) {
    val f1 = calculate('+')
    println(f1(10, 5))  // 15
    
    val f2 = calculate('-')
    println(f2(10, 5))  // 5
    
    val f3 = calculate('*')
    println(f3(10, 5))  // 50
    
    val f4 = calculate('/')
    println(f4(10, 5))  // 2
}

??上面代碼fun calculate(opr: Char)后面的(Int, Int) -> Int返回值類型(函數(shù)類型),而不是 Lambda 表達(dá)式。

??提示:Lambda 表達(dá)式與函數(shù)、匿名函數(shù)一樣都有函數(shù)類型,但從 Lambda 表達(dá)式的定義中只能看到參數(shù)類型,看不到返回類型聲明,那是因?yàn)榉祷仡愋涂梢酝ㄟ^(guò)上下文推導(dǎo)出來(lái)。

二、使用Lambda 表達(dá)式

??Lambda 表達(dá)式也是函數(shù)類型,可以聲明變量,也可以作為其他函數(shù)的參數(shù)或者返回值使用。

fun calculatePrint(a: Int, b: Int, opr: Char, func: (Int, Int) -> Int) {
    println("$a $opr $b = ${func(a, b)}")
}

fun main(args: Array<String>) {
    calculatePrint(10, 5, '+', { a, b -> a + b }) // Lambda作為參數(shù)
    calculatePrint(10, 5, '-', func = { a, b -> a - b }) // Lambda作為參數(shù)(命名參數(shù)方式傳參)
}

三、Lambda 表達(dá)式簡(jiǎn)化寫法

??kotlin 提供了多種 Lambda 表達(dá)式的簡(jiǎn)化寫法。

1、??參數(shù)類型推導(dǎo)簡(jiǎn)化

??kotlin 編譯期可以根據(jù)上下文環(huán)境推導(dǎo)出參數(shù)類型和返回值類型。例如上面代碼定義的高階函數(shù) calculate(opr: Char) 返回值類型為 (Int, Int) -> Int,下面給出此高階函數(shù)的 Lambda 表達(dá)式的標(biāo)準(zhǔn)版 和 簡(jiǎn)化版:

// 標(biāo)準(zhǔn)類型的Lambda表達(dá)式
{ a: Int, b: Int -> a + b}

??kotlin 可以根據(jù)高階函數(shù)calculate的返回值類型(Int, Int) -> Int,推斷出 Lambda 表達(dá)式參數(shù)為 Int 類型,返回值類型也為 Int 類型。

// 簡(jiǎn)化版
{ a, b -> a + b}

??完整的簡(jiǎn)化版 calculate 代碼:

fun calculate(opr: Char): (Int, Int) -> Int = when (opr) {
    '+' -> { a, b -> a + b }
    '-' -> { a, b -> a - b }
    '*' -> { a, b -> a * b }
    else -> { a, b -> a / b }
}

2、??使用尾隨 Lambda 表達(dá)式

??如果一個(gè)函數(shù)的最后一個(gè)參數(shù)是 Lambda 表達(dá)式,那么這個(gè) Lambda 表達(dá)式可以放到函數(shù)括號(hào)之后

fun calculatePrint(a: Int, b: Int, opr: Char, func: (Int, Int) -> Int) {
    println("$a $opr $b = ${func(a, b)}")
}

fun calculatePrint105(func: (Int, Int) -> Int) {
    println("${func(10, 5)}")
}

fun main(args: Array<String>) {
    // 標(biāo)準(zhǔn)調(diào)用
    calculatePrint(10, 5, '+', { a, b -> a + b })
    calculatePrint105({ a, b -> a + b })

    // 尾隨Lambda表達(dá)式
    calculatePrint(10, 5, '+') { a, b -> a + b } // 1??
    calculatePrint105() { a, b -> a + b } // 2??

    // calculatePrint105(){a, b -> a + b} 再簡(jiǎn)化版本
    calculatePrint105 { a, b -> a + b }  // 尾隨Lambda表達(dá)式,如果沒(méi)有參數(shù)可以省略括號(hào)
}

??注意:尾隨 Lambda 表達(dá)式容易被誤認(rèn)為是函數(shù)聲明,如上述代碼第1??、2??行,是不是會(huì)認(rèn)為是一個(gè)函數(shù)?但它缺少函數(shù)聲明的必要關(guān)鍵字 fun,同時(shí)也缺少參數(shù)類型,命名規(guī)定等。

3、??省略參數(shù)聲明

??如果 Lambda 表達(dá)式的參數(shù)只有一個(gè),并且能夠根據(jù)上下文環(huán)境推導(dǎo)出它的數(shù)據(jù)類型,那么這個(gè)參數(shù)聲明可以省略,在 Lambda 體中使用隱式參數(shù) it 替代 Lambda 表達(dá)式的參數(shù)。

fun reversePrint(word: String, func: (String) -> String) {
    println("${func(word)}")
}

fun main(args: Array<String>) {
    // 未省略參數(shù)說(shuō)明
    reversePrint("hello") { word -> word.reversed() }
    // 省略參數(shù)說(shuō)明
    reversePrint("hello") { it.reversed() }
    
    val result1: (String) -> Unit = { print(it) } // 3??省略參數(shù)說(shuō)明,并且可以根據(jù)result1聲明的函數(shù)類型,判斷出隱式參數(shù)it的類型
    val result2 = { a: String -> print(a) } // 4??不能省略參數(shù)說(shuō)明,無(wú)法根據(jù)上下環(huán)境判斷出參數(shù)類型
}

??注意:Lambda體中 it 隱式變量是由 kotlin 編譯器生成的,它的使用有兩個(gè)前提:一是 Lambda 表達(dá)式只有一個(gè)參數(shù),二是根據(jù)上下文環(huán)境能夠推導(dǎo)出參數(shù)類型。比較代碼第3??、4??行會(huì)發(fā)現(xiàn),代碼第4??行由于 result2 沒(méi)有指定數(shù)據(jù)類型,編輯器不能推導(dǎo)出 Lambda 表達(dá)式的參數(shù)類型,所以不能使用 it。而代碼第3??行,由于 result1 指定了數(shù)據(jù)類型為 (String) -> Unit,編輯器能夠推導(dǎo)出 Lambda 表達(dá)式的參數(shù)類型,所以可以使用 it。

四、Lambda 表達(dá)式與 return 語(yǔ)句

??Lambda 表達(dá)式可以使用 return 語(yǔ)句,它會(huì)使程序跳出 Lambda 表達(dá)式體;
??例如:函數(shù) sum 內(nèi)部有一個(gè) Lambda 表達(dá)式,在 Lambda 表達(dá)式內(nèi)執(zhí)行 return 語(yǔ)句,則會(huì)直接結(jié)束 sum 函數(shù),而 return語(yǔ)句的返回值,作為了 sum 函數(shù)的返回值。

fun sum(vararg num: Int): Int {
    var total = 0
    num.forEach {
        if (it == 10) return -1
        total += it
    }
    return total
}

fun main(args: Array<String>) {
    val n = sum(1, 2, 10, 3)
    println(n)
}

2019-06-05 12:48:22.000 17599-17599/cn.ak.kot I/System.out: -1

??那么像上面的代碼能通過(guò) return 結(jié)束 Lambda 表達(dá)式嗎?當(dāng)然是可以的,但需要在 return 時(shí)指定返回標(biāo)簽,返回標(biāo)簽用法在Kotlin基礎(chǔ)認(rèn)識(shí) (8)程序流程控制中有介紹。修改后的代碼如下:

fun sum(vararg num: Int): Int {
    var total = 0
    num.forEach {
        if (it == 10) return@forEach // 5??@forEach是隱式聲明標(biāo)簽
        total += it
    }
    return total
}

fun main(args: Array<String>) {
    val n = sum(1, 2, 10, 3)
    println(n)
}

2019-06-05 14:18:09.269 22302-22302/cn.ak.kot I/System.out: 6

??上述代碼第5??行是使用隱式標(biāo)簽 @forEach 結(jié)束本次 Lambda 表達(dá)式運(yùn)行。

??提示:forEach 是集合、數(shù)組或區(qū)間的函數(shù),它后面是一個(gè) Lambda 表達(dá)式,集合、數(shù)組或區(qū)間對(duì)象調(diào)用 forEach 函數(shù)時(shí),會(huì)將它們的每一個(gè)元素傳遞給 Lambda 表達(dá)式并執(zhí)行。

??下面是一個(gè)顯示使用標(biāo)簽的案例:

fun main(args: Array<String>) {
    val add = label@ {    // 打一個(gè)標(biāo)簽,標(biāo)記結(jié)束位置
        val a = 1
        val b = 2
        return@label 10 // 結(jié)束運(yùn)行,返回到標(biāo)簽位置
        a + b   // 這句代碼實(shí)際是永遠(yuǎn)執(zhí)行不到的
    }
    println(add())
}

2019-06-05 14:25:10.968 23002-23002/cn.ak.kot I/System.out: 10
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容