高阶函数

576 阅读3分钟

定义高阶函数

在此之前我们学习过map、filter、run、apply函数等,这些函数的共同特点就是都会让我们传入一个Lambda表达式作为参数,像这种接收Lambda参数的函数称为具有函数式编程风格的API,但如果要定义自己的函数式API,就得借助高阶函数来实现了。

那么高阶函数是什么?

高阶函数其实就是一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数。 函数类型定义如下: (String, Int) -> Unit

->左边的部分用来声明该函数接收什么参数,多个参数之间用逗号隔开,如果不接受任何参数,写一对空括号即可。

->右边的部分用于声明该函数的返回值是什么类型,如果没有返回值就是用Unit,大致相当于Java中的void。

具体例子如下:

fun example(func: (String, Int) -> Unit) {
    func("hello", 123)
}

定义一个叫num1AndNum2()的高阶函数

fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    val result = operation(num1, num2)
    return result
}

定义如下函数,这里的两个函数的参数声明和返回值声明都和num1AndNum2()函数中的函数类型参数是完全匹配的:

fun plus(num1: Int, num2: Int): Int {
    return num1 + num2
}

fun minus(num1: Int, num2: Int): Int {
    return num1 - num2
}

主函数:

fun main() {
    val num1 = 100
    val num2 = 80
    val result1 = num1AndNum2(num1, num2, ::plus)
    val result2 = num1AndNum2(num1, num2, ::minus)
    println("result1 is $result1")
    println("result2 is $result2")
}

第三个参数使用了::plus和::minus这种写法,这是一种函数引用方式的写法。将plus()和minus()函数作为参数传递给num1AndNum2()函数。

这种函数写法虽然能使用,但是如果每次调用任何高阶函数的时候都还得先定义一个与其函数类型参数相匹配的函数,这太麻烦了。

Lambda表达式是最常见也是最普通的高阶函数调用方式。

fun main() {
    val num1 = 100
    val num2 = 80
    val result1 = num1AndNum2(num1, num2) { n1, n2 -> n1 + n2 }
    val result2 = num1AndNum2(num1, num2) { n1, n2 -> n1 - n2 }
    println("result1 is $result1")
    println("result2 is $result2")
}

apply函数能用于给Lambda表达式提供一个指定的上下文,当需要连续调用同一个对象的多个方法时,apply函数可以让代码变得更加精简。

接下来我们使用高阶函数模仿实现一个类似的功能。

fun StringBuilder.build(block:StringBuilder.()->Unit):StringBuilder {
    block()
    return this
}

给StringBuilder类定义了一个build扩展函数,这个扩展函数接收一个函数类型参数,返回值也是StringBuilder。

在空括号之前加上了一个StringBuilder.的结构,这是什么意思呢?其实这才是定义高阶函数完整的语法规则,在函数类型的前面加上ClassName,就表示这个函数类型是定义在哪个类当中。那我们把函数类型定义到StringBuilder类当中有什么好处呢?好处就是当我们调用build函数时传入的Lambda表达式将会自动拥有StringBuilder的上下文,同时这也是apply函数的实现方式。

fun main() {
    val num1 = 100
    val num2 = 80
//    val result1 = num1AndNum2(num1, num2, ::plus)
//    val result2 = num1AndNum2(num1, num2, ::minus)
    val result1 = num1AndNum2(num1, num2) { n1, n2 -> n1+n2}
    val result2 = num1AndNum2(num1, num2) {n1,n2->n1-n2}
    println("result1 is $result1")
    println("result2 is $result2")


    val list = listOf("Apple","Banana","Orange","Pear","Grape")
    val result = StringBuilder().build {
        append("Start eating fruits.\n")
        for (fruit in list){
            append(fruit).append("\n")
        }
        append("Ate all fruits.")
    }
    println(result.toString())
}