第14章 函数式编程基石——高阶函数和Lambda表达式
函数式编程思想虽然与面向对象一样立即悠久,但是支持函数式编程的计算机语言不过是近几年的事情。这些语言有Swift、Python、Java 8和C++11等,作为新生的语言Kotlin也支持函数式编程。本章将介绍Kotlin语言中函数式编程最重要的基础知识——高阶函数和Lambda表达式。
14.1 函数式编程简介
函数式编程(functional programming)与面向对象编程一样都一种编程范式,函数式编程,也称为面向函数的编程。在函数式编程中一切都是函数。
函数式编程核心概念如下:
o 函数是“一等公民”:是指函数与其他数据类型是一样的,处于平等的地位。函数可以作为其他函数的参数传入,也可以作为其他函数的返回值返回。
o 使用表达式,不用语句:函数式编程关心的输入和输出,即:参数和返回值。在程序中使用表达式可以有返回值,而语句没有。例如:控制结构中的if和when结构都属于表达式。
o 高阶函数:函数式编程支持高阶函数,所谓高阶函数就是一个函数可以作为另外一个函数的参数或返回值。
o 无副作用:是指函数执行过程会返回一个结果,不会修改外部变量,这就是“纯函数”,同样的输入参数一定会有同样的输出结果。
Kotlin语言支持函数式编程,提供了函数类型、高阶函数和Lambda表达式。
14.2 高阶函数
函数式编程的关键是高阶函数的支持。一个函数可以作为另一个函数的参数,或者返回值,那么这个函数就是“高阶函数”。本节介绍一下高阶函数。
14.2.1 函数类型
Kotlin中每一个函数都有一个类型,称为“函数类型”,函数类型作为一种数据类型与数据类型在使用场景没有区别。可以声明变量,也可以作为其他函数的参数或者其他函数的返回值使用。
现有如下3个函数的定义:
//代码文件:chapter14/src/com/a51work6/section2/ch14.2.1.kt
package com.a51work6.section2
//定义计算长方形面积函数
//函数类型(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 ②
fun sayHello() { //函数类型()->Unit ③
print(“Hello, World”)
}
fun main(args: Array) {
val getArea: (Double, Double) ->Double = ::triangleArea ④
//调用函数
val area = getArea(50.0, 40.0) ⑤
print(area) //1000.0
}
上述代码中,函数rectangleArea和triangleArea具有相同的函数类型(Double, Double) -> Double。函数类型就是把函数参数列表中的参数类型保留下来,再加上箭头符号和返回类型,形式如下:
参数列表中的参数类型 -> 返回类型
每一个函数都有函数类型,即便是函数列表中没有参数,以及没有返回值的函数也有函数类型,如代码第③行的sayHello()函数,sayHello()函数的函数类型是()->Unit。
14.2.2 函数字面量
函数类型可以声明的变量,那么函数类型变量能够接收什么的数据呢?即函数字面量如何表示的问题,函数字面量可以有三种表示:
函数引用。引用到一个已经定义好的,有名字的函数。它可以作为函数字面量。
匿名函数。没有名字的函数,即匿名函数,它也可以作为函数字面量。
Lambda表达式。Lambda表达式是一种匿名函数,可以作为函数字面量。
示例代码如下:
//代码文件:chapter14/src/com/a51work6/section2/ch14.2.2.kt
package com.a51work6.section2
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 ②
'*' -> {
//乘法匿名函数
fun(a: Int, b: Int):Int { ③
return (a * b)
}
}
else -> { a, b ->(a / b) } //除法Lambda表达式 ④
}
return result
}
fun main(args: Array) {
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))
}
上述代码第①行和第②行是函数引用,采用“双冒号加函数名”形式引用,add和sub是两个局部函数,它们的函数引用表示方式是::add和::sub,它们可以作为函数字面量赋值给result变量。代码第③行声明匿名函数,匿名函数不需要函数名,它是一个表达式直接赋值给result变量。代码第④行采用的Lambda表达式,也可以赋值给result变量。
获得一个函数类型的变量之后如何使用呢?答案是可以把它当作函数一样调用。例如代码第⑤行val f1 = calculate(’+’)中f1是一个函数类型变量,事实上f1就是指向add函数的。代码第⑥行是调用f1函数类型变量,事实上就是在调用add函数。其他的变量以此类推,不再赘述。
14.2.3 函数作为另一个函数返回值使用
可以把函数作为另一个函数的返回值使用,那么这个函数属于高阶函数。14.2.2节的calculate函数的返回类型就是(Int, Int) -> Int函数类型,说明calculate是高阶函数。下面再介绍一个函数作为另一个函数返回值使用的示例:
//代码文件:chapter14/src/com/a51work6/section2/ch14.2.3.kt
package com.a51work6.section2
fun getArea(type: String): (Double, Double) -> Double { ①
var returnFunction: (Double, Double)-> Double ②
when (type) {
"rect" -> //rect 表示长方形
returnFun