Android开发之Kotlin从入门到精通总结(三)---函数式编程基石——高阶函数和Lambda表达式
2021/4/19 20:25:41
本文主要是介绍Android开发之Kotlin从入门到精通总结(三)---函数式编程基石——高阶函数和Lambda表达式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
(一)、函数式编程简介
1.1 函数式编程(functional programming)
函数式编程是一种编程典范,也就是面向函数的编程。 在函数式编程中一切都是函数。 函数式编程核心概念如下: ① 函数是 “ 一等公民 ” :是指函数与其他数据类型(Int、Double、String等)是一样的,处于平等的地位。函数可以作为其他函数的参数传入,也可以作为其他函数的返回值返回。 ② 使用表达式,不用语句:函数式编程关心的输入和输出,即:参数和返回值。在程序中使用表达式可以有返回值,而语句没有。例如:控制结构中的if 和 when 结构都属于表达式。 ③ 高阶函数:函数式编程支持高阶函数,所谓高阶函数就是一个函数可以作为另外一个函数的参数或返回值。 ④ 无副作用:是指函数执行过程会返回一个结果,不会修改外部变量,这就是 “ 纯函数” ,同样的输入参数一定会有同样的输出结果。 Kotlin 语言支持函数式编程,提供了函数类型、高阶函数和 Lambda 表达式。1.2 高阶函数 和 函数类型
① 高阶函数的概念:函数式编程的关键是高阶函数的支持。一个函数可以作为另一个函数的参数,或者返回值,那么这个函数就是“高阶函数 ” 。 ② 函数类型概念:Kotlin中每一个函数都有一个类型,称为 “ 函数类型 ” ,函数类型作为一种数据类型与数据类型在使用场景没有区别。可以声明变量,也可以作为其他函数的参数或者其他函数的 返回值使用。函数类型就是把函数参数列表中的参数类型保留下来,再加上箭头符号和返回类型,形式如下:参数列表中的参数类型 -> 返回类型
举例:
package com.work.section //定义计算长方形面积函数 //函数类型: (Double, Double) -> Double fun rectangleArea(width: Double, height: Double): Double { return width * height } //定义计算三角形面积函数 //函数类型: (Double, Double) -> Double fun triangleArea(bottom: Double, height: Double) = 0.5 * bottom * height //函数类型: ()->Unit fun sayHello() { print("Hello, World") } fun main(args: Array<String>) { val getArea: (Double, Double) -> Double = ::triangleArea //调用函数 val area = getArea(50.0, 40.0) print(area) //1000.0 }
每一个函数都有函数类型,即便是函数列表中没有参数,以及没有返回值的函数也有函数类型,如代码sayHello()函数,sayHello()函数的函数类型是()->Unit。
1.3 函数字面量
函数类型可以声明的变量,那么函数类型变量能够接收什么的数据呢?即函数字面量如何表示的问题,函数字面量可以有三种表示: ① 函数引用。引用到一个已经定义好的,有名字的函数。它可以作为函数字面量。 ② 匿名函数。没有名字的函数,即匿名函数,它也可以作为函数字面量。 ③ Lambda 表达式。 Lambda 表达式是一种匿名函数,可以作为函数字面量。package com.work.section //该函数的返回值类型是:函数类型,即(Int, Int) -> Int fun calculate(opr: Char): (Int, Int) -> Int { //加法函数 fun add(a: Int, b: Int): Int { return a + b } //减法函数 fun sub(a: Int, b: Int): Int { return a - b } val result: (Int, Int) -> Int = when (opr) { //函数引用,采用“双冒号加函数名”形式引用,add和sub是两个局部函数,它们的函数引用表示方式是::add和::sub '+' -> ::add '-' -> ::sub //匿名函数 '*' -> { //乘法匿名函数 fun(a: Int, b: Int): Int { return (a * b) } } else -> { a, b -> (a / b) } //除法Lambda表达式 } return result } fun main(args: Array<String>) { //f0的类型是函数类型,即(Int, Int) -> Int val f0:(Int, Int) -> Int = calculate('+') println(f0(10, 5)) //调用f0变量 //如果表达式返回类型确定了,则可以省略变量后的类型声明 val f1 = calculate('+') println(f1(10, 5)) //调用f1变量 //f2的类型是函数类型,即(Int, Int) -> Int val f2 = calculate('-') println(f2(10, 5)) //f3的类型是函数类型,即(Int, Int) -> Int val f3 = calculate('*') println(f3(10, 5)) //f4的类型是函数类型,即(Int, Int) -> Int val f4 = calculate('/') println(f4(10, 5)) }
其中,calculate函数的返回类型就是(Int, Int) -> Int函数类型,说明calculate是高阶函数。
输出结果:
2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: --------函数类型输出 加、加、减、乘、除------- 2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: 15 2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: 15 2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: 5 2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: 50 2021-04-19 15:21:48.616 17243-17243/com.xw.kotlinforandroid I/System.out: 2
1.4 函数作为另一个函数返回值使用
可以把函数作为另一个函数的返回值使用,那么这个函数属于高阶函数。
package com.work.section //定义计算长方形面积函数 //函数类型: (Double, Double) -> Double fun rectangleArea(width: Double, height: Double): Double { return width * height } //定义计算三角形面积函数 //函数类型: (Double, Double) -> Double fun triangleArea(bottom: Double, height: Double) = 0.5 * bottom * height //返回类型为函数类型,即 (Double, Double) -> Double fun getArea(type: String): (Double, Double) -> Double { var returnFunction: (Double, Double) -> Double; when (type) { "rect" -> //rect 表示长方形 returnFunction = ::rectangleArea else -> //tria 表示三角形 returnFunction = ::triangleArea } return returnFunction } fun main(args: Array<String>) { //获得计算三角形面积函数 var area: (Double, Double) -> Double = getArea("tria") println("底10 高13,计算三角形面积:${area(10.0, 15.0)}") //获得计算长方形面积函数 area = getArea("rect") println("宽10 高15,计算长方形面积:${area(10.0, 15.0)}") }
1.5 函数作为参数使用
作为高阶函数还可以接收另一个函数作为参数使用。//定义计算长方形面积函数 //函数类型: (Double, Double) -> Double fun rectangleArea(width: Double, height: Double): Double { return width * height } //定义计算三角形面积函数 //函数类型: (Double, Double) -> Double fun triangleArea(bottom: Double, height: Double) = 0.5 * bottom * height //高阶函数,funcName参数是函数类型:(Double, Double) -> Double fun getAreaByFunc(funcName: (Double, Double) -> Double, a: Double, b: Double): Double { return funcName(a, b) } fun main(args: Array<String>) { //获得计算三角形面积函数 var result = getAreaByFunc(::triangleArea, 10.0, 15.0) ② println("底10 高15,计算三角形面积:$result") ③ //获得计算长方形面积函数 result = getAreaByFunc(::rectangleArea, 10.0, 15.0) ④ println("宽10 高15,计算长方形面积:$result") ⑤ }
运行结果:
底10 高15,三角形面积:75.0 宽10 高15,计算长方形面积:150.0
(二)lambda表达式
2.1 Lambda表达式
Lambda 表达式是一种匿名函数,可以作为表达式、函数参数和函数返回值使用,Lambda 表达式的运算结果是一个函数。 Lambda表达式标准语法格式 :Kotlin中的 Lambda 表达式很灵活,其标准语法格式如下:{ 参数列表 -> Lambda体 }其中, Lambda 表达式的参数列表与函数的参数列表形式类似,但是 Lambda 表达式参数列表前后没有小括号。箭头符号将参数列表与Lambda 体分隔开, Lambda 表达式不需要声明返回类型。 Lambda 表达式可以有返回值,如果没有 return 语句 Lambda 体的最后一个表达式就是Lambda 表达式的返回值,如果有 return 语句返回值是 return 语句后面的表达式。 提示: Lambda 表达式与函数、匿名函数一样都有函数类型,但从 Lambda 表达式的定义中只能看到参数类型,看不到返回类型声明,那是因为返回类型可以通过上下文推导出来。
private fun calculate(opr: Char): (Int, Int) -> Int { return when (opr) { //lambda表达式 '+' -> { a: Int, b: Int -> a + b } '-' -> { a: Int, b: Int -> a - b } '*' -> { a: Int, b: Int -> a * b } else -> { a: Int, b: Int -> a / b } } } fun main(args: Array<String>) { val f1 = calculate('+') println(f1(10, 5)) //调用f1变量 val f2 = calculate('-') println(f2(10, 5)) val f3 = calculate('*') println(f3(10, 5)) val f4 = calculate('/') println(f4(10, 5)) }
Lambda表达式也是函数类型,可以声明变量,也可以作为其他函数的参数或者返回值使用。
2.2 lambda表达式的简化用法
①参数类型推导简化 类型推导是 Kotlin 的强项, Kotlin 编译器可以根据上下文环境推导出参数类型和返回值类型。以下代码是标准形式的Lambda 表达式:{ a: Int, b: Int -> a + b }
Kotlin能推导出参数a和b是Int类型,当然返回值也是Int类型。简化形式如下:
{ a, b -> a + b }
② 使用尾随Lambda表达式
Lambda表达式可以作为函数的参数传递,如果Lambda表达式很长,就会影响程序的可读性。如果一个函数的最后一个参数是Lambda表达式,那么这个Lambda表达式可以放在函数括号之后(注意这里所说的lambda表达式是包括花括号"{}"的)。示例代码如下:
fun calculatePrint1(funN: (Int, Int) -> Int) { //参数是函数类型 //使用funN参数 println("${funN(10, 5)}") } //打印计算结果函数 最后一个参数是函数类型:(Int, Int) -> Int fun calculatePrint(n1: Int, n2: Int, opr: Char, funN: (Int, Int) -> Int) { println("${n1} ${opr} ${n2} = ${funN(n1, n2)}") } fun main(args: Array<String>) { calculatePrint(10, 5, '+', { a, b -> a + b })//标准形式 calculatePrint(10, 5, '-') { a, b -> a - b }//尾随Lambda表达式形式 //最后一个参数是lambda表达式:{ a, b -> a + b } calculatePrint1({ a, b -> a + b })//标准形式 calculatePrint1() { a, b -> a + b }//尾随Lambda表达式形式 calculatePrint1 { a, b -> a + b }//尾随Lambda表达式,如果小括号里没有参数可省略小括号 }
注意:由于calculatePrint1函数只有一个lambda表达式作为参数,它采用了尾随Lambda表达式形式,这样一来它的小括号中就没有参数了,这种情况下可以省略小括号。
③ 省略参数声明
如果Lambda表达式的参数只有一个,并且能够根据上下文环境推导出它的数据类型,那么这个参数声明可以省略,在Lambda体中使用隐式参数it替代Lambda表达式的参数。
fun revreseAndPrint(str: String, funN: (String) -> String) { val result = funN(str) println(result) } fun main(args: Array<String>) { revreseAndPrint("hello", { s -> s.reversed() })//标准形式 revreseAndPrint("hello", { it.reversed() })//省略参数,使用隐式参数it //由于result1被未指定数据类型,编译器不能推导出来Lambda表达式的参数类型,所以不能使用it。 val result1 = { a: Int -> println(a) }//不能省略参数声明 //由于result2被指定了数据类型(Int)->Unit,编译器能推导出 Lambda表达式的参数类型,所以可以使用it。 val result2:(Int)->Unit = { println(it) }//可以省略参数声明 result2(30) //输出结果是30 }
注意 Lambda体中it隐式变量是由Kotlin编译器生成的,它的使用有两个前提:一是Lambda表达式只有一个参数,二是根据上下文能够推导出参数类型。
④lambda表达式与return语句
Lambda 表达式体中也可以使用 return 语句,它会使程序跳出 Lambda表达式体。 提示 forEach 是集合、数组或区间的函数,如果它后面是一个 Lambda 表达式,集合、数组或区间对象调用forEach 函数时,会将它们的每一个元素传递给 Lambda 表达式并执行。 示例代码如下://累加求和函数 fun sum(vararg num: Int): Int { var total = 0 //forEach后面跟的是一个lambda表达式 num.forEach { //if (it == 10) return -1 //返回到最近的函数,即sum函数 //@forEach是隐式声明标签,标签名是Lambda表达式所在函数名(forEach)。 if (it == 10) return@forEach//返回Lambda表达式函数 total += it } return total } fun main(args: Array<String>) { val n = sum(1, 2, 10, 3) println(n) //6 //label@是Lambda表达式显示声明标签 val add = label@ { val a = 1 val b = 2 return@label 6666 a + b } //调用Lambda表达式add println(add()) //6666 }
上述代码num.forEach是使用了forEach函数,它后面的Lambda表达式,如果使用代码 if (it == 10) return -1语句,则会返回最近的函数,即sum函数,不是返回 Lambda表达式forEach。为了返回Lambda表达式则需要在return语句后面加上标签,即“if (it == 10) return@forEach”,@forEach是隐式声明标签,标签名是Lambda表达式所在函数名(forEach)。也可以为Lambda表达式声明显示标签,代码label@是Lambda表达式显示声明标签,代码“ return@label 10 ”是使用显示标签。
2021-04-19 18:04:19.279 28687-28687/com.xw.kotlinforandroid I/System.out: 6 2021-04-19 18:04:19.279 28687-28687/com.xw.kotlinforandroid I/System.out: 6666
这篇关于Android开发之Kotlin从入门到精通总结(三)---函数式编程基石——高阶函数和Lambda表达式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-01-06Kotlin委托属性(1)
- 2023-06-15Kotlin协程-那些理不清乱不明的关系
- 2023-06-08[Kotlin Tutorials 21] 协程的取消
- 2023-05-26Kotlin难点
- 2023-02-23【备战春招】第16天 Kotlin实用技巧
- 2023-02-23【备战春招】第15天 Kotlin扩展Extensions技术探秘
- 2023-02-22【备战春招】第14天 深入理解Kotlin注解
- 2023-02-21【备战春招】第12天 深入理解Kotlin类与接口
- 2023-02-21【备战春招】第13天 深入理解Kotlin泛型
- 2023-02-18【备战春招】第10天 Kotlin方法与Lambda表达式