美文网首页
Kotlin函数式编程 (2)✔️Lambda表达式

Kotlin函数式编程 (2)✔️Lambda表达式

作者: 狼性代码人 | 来源:发表于2019-06-05 14:28 被阅读0次
  • Lambda 表达式标准语法格式
  • 使用Lambda 表达式
  • Lambda 表达式简化写法
    • ✔️参数类型推导简化
    • ✔️使用尾随 Lambda 表达式
    • ✔️省略参数声明
  • Lambda 表达式与 return 语句

  Lambda 表达式是一种匿名函数,可以作为表达式函数参数函数返回值 使用。

一、Lambda 表达式标准语法格式

  Lambda 表达式的语法很灵活,下面只是它的标准语法格式:

{ 参数列表 ->
    Lambda 体
}

  Lambda 表达式的参数列表与函数的参数列表形式类似,但是 Lambda 表达式参数列表前后 没有小括号() 。箭头符号将参数列表和 Lambda 体分隔开,Lambda 表达式不需要声明返回类型。Lambda 表达式可以有返回值,如果 没有 return 语句,Lambda 体的最后一个表达式就是 Lambda 表达式的返回值;如果 有 return 语句,返回值是 return 语句后面的表达式。

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

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返回值类型(函数类型),而不是 Lambda 表达式。

  提示:Lambda 表达式与函数、匿名函数一样都有函数类型,但从 Lambda 表达式的定义中只能看到参数类型,看不到返回类型声明,那是因为返回类型可以通过上下文推导出来。

二、使用Lambda 表达式

  Lambda 表达式也是函数类型,可以声明变量,也可以作为其他函数的参数或者返回值使用。

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作为参数
    calculatePrint(10, 5, '-', func = { a, b -> a - b }) // Lambda作为参数(命名参数方式传参)
}

三、Lambda 表达式简化写法

  kotlin 提供了多种 Lambda 表达式的简化写法。

1、✔️参数类型推导简化

  kotlin 编译期可以根据上下文环境推导出参数类型和返回值类型。例如上面代码定义的高阶函数 calculate(opr: Char) 返回值类型为 (Int, Int) -> Int,下面给出此高阶函数的 Lambda 表达式的标准版 和 简化版:

// 标准类型的Lambda表达式
{ a: Int, b: Int -> a + b}

  kotlin 可以根据高阶函数calculate的返回值类型(Int, Int) -> Int,推断出 Lambda 表达式参数为 Int 类型,返回值类型也为 Int 类型。

// 简化版
{ a, b -> a + b}

  完整的简化版 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 表达式

  如果一个函数的最后一个参数是 Lambda 表达式,那么这个 Lambda 表达式可以放到函数括号之后

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>) {
    // 标准调用
    calculatePrint(10, 5, '+', { a, b -> a + b })
    calculatePrint105({ a, b -> a + b })

    // 尾随Lambda表达式
    calculatePrint(10, 5, '+') { a, b -> a + b } // 1️⃣
    calculatePrint105() { a, b -> a + b } // 2️⃣

    // calculatePrint105(){a, b -> a + b} 再简化版本
    calculatePrint105 { a, b -> a + b }  // 尾随Lambda表达式,如果没有参数可以省略括号
}

  注意:尾随 Lambda 表达式容易被误认为是函数声明,如上述代码第1️⃣、2️⃣行,是不是会认为是一个函数?但它缺少函数声明的必要关键字 fun,同时也缺少参数类型,命名规定等。

3、✔️省略参数声明

  如果 Lambda 表达式的参数只有一个,并且能够根据上下文环境推导出它的数据类型,那么这个参数声明可以省略,在 Lambda 体中使用隐式参数 it 替代 Lambda 表达式的参数。

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

fun main(args: Array<String>) {
    // 未省略参数说明
    reversePrint("hello") { word -> word.reversed() }
    // 省略参数说明
    reversePrint("hello") { it.reversed() }
    
    val result1: (String) -> Unit = { print(it) } // 3️⃣省略参数说明,并且可以根据result1声明的函数类型,判断出隐式参数it的类型
    val result2 = { a: String -> print(a) } // 4️⃣不能省略参数说明,无法根据上下环境判断出参数类型
}

  注意:Lambda体中 it 隐式变量是由 kotlin 编译器生成的,它的使用有两个前提:一是 Lambda 表达式只有一个参数,二是根据上下文环境能够推导出参数类型。比较代码第3️⃣、4️⃣行会发现,代码第4️⃣行由于 result2 没有指定数据类型,编辑器不能推导出 Lambda 表达式的参数类型,所以不能使用 it。而代码第3️⃣行,由于 result1 指定了数据类型为 (String) -> Unit,编辑器能够推导出 Lambda 表达式的参数类型,所以可以使用 it

四、Lambda 表达式与 return 语句

  Lambda 表达式可以使用 return 语句,它会使程序跳出 Lambda 表达式体;
  例如:函数 sum 内部有一个 Lambda 表达式,在 Lambda 表达式内执行 return 语句,则会直接结束 sum 函数,而 return语句的返回值,作为了 sum 函数的返回值。

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

  那么像上面的代码能通过 return 结束 Lambda 表达式吗?当然是可以的,但需要在 return 时指定返回标签,返回标签用法在Kotlin基础认识 (8)程序流程控制中有介绍。修改后的代码如下:

fun sum(vararg num: Int): Int {
    var total = 0
    num.forEach {
        if (it == 10) return@forEach // 5️⃣@forEach是隐式声明标签
        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️⃣行是使用隐式标签 @forEach 结束本次 Lambda 表达式运行。

  提示:forEach 是集合、数组或区间的函数,它后面是一个 Lambda 表达式,集合、数组或区间对象调用 forEach 函数时,会将它们的每一个元素传递给 Lambda 表达式并执行。

  下面是一个显示使用标签的案例:

fun main(args: Array<String>) {
    val add = label@ {    // 打一个标签,标记结束位置
        val a = 1
        val b = 2
        return@label 10 // 结束运行,返回到标签位置
        a + b   // 这句代码实际是永远执行不到的
    }
    println(add())
}

2019-06-05 14:25:10.968 23002-23002/cn.ak.kot I/System.out: 10

相关文章

网友评论

      本文标题:Kotlin函数式编程 (2)✔️Lambda表达式

      本文链接:https://www.haomeiwen.com/subject/xijlxctx.html