Kotlin 中的高阶函数

Niyati Thakkar 2024年2月15日
  1. Kotlin 中的高阶函数
  2. 在 Kotlin 中将函数作为参数传递
  3. Kotlin 函数作为返回值
Kotlin 中的高阶函数

本教程教授如何在 Kotlin 中使用函数将函数作为参数传递并返回函数。

Kotlin 中的函数非常灵活,它们可以存储在数据结构中、存储在变量中、作为参数传递给函数或由另一个函数返回。他们可以做非函数变量所做的一切。

Kotlin 中的高阶函数

Kotlin 中的高阶函数是返回函数或将函数作为参数或两者兼有的函数。换句话说,在 Kotlin 中,你可以有一个函数,它接受另一个函数作为参数,返回一个函数作为结果,或者两者都做,这些函数称为高阶函数。

Kotlin 中的高阶函数

让我们看一个例子来看看它是如何工作的。

fun main(){

    fun hello() {
        println("In hello!")
    }

    fun random(func : () -> Unit){
        println("In random!")
        println("calling hello")
        func()
    }

    random(::hello)
}

输出:

In random!
calling hello
In hello!

解释:

  1. 首先,我们在主函数中有另外两个函数,hellorandom

  2. hello() 不带参数并且不返回任何值(即返回 Unit)。

  3. random() 采用一个参数,即在 random 的范围内本地称为 func 的函数,并且不返回任何值。func() 函数不接受任何参数并返回一个 Unit,这意味着它不返回值。

    因此,func 是在调用 random 时作为参数传递给函数的本地名称,:(冒号)通常写在变量名称之后,以区分行为和变量名称,在 () func 函数所需的括号参数被写入,并且在 -> 之后需要函数的返回类型。

  4. 最后,我们将 hello 函数传递给 main 方法中的 random

让我们看看输出以了解程序的流程。

首先,当我们从将 hello 作为参数传递的 main 函数中调用 random 时,我们在 random 函数内部。因此,我们从随机打印的上述两行。

现在 random 称为 func,它只是 hello 函数,因为我们将它作为参数传递,因此在最后一行,它在 hello 函数中。

Kotlin 中的 Lambda 表达式

Lambda 表达式是函数文字或匿名函数,我们可以在其中以简短易读的方式编写函数。它们还使代码看起来紧凑而干净。

例如:

val sum = {num1: Int , num2: Int -> num1 + num2}

sum 采用两个 Int 变量,num1num2,并返回一个 Int num1+num2(即两个数字的和)。

在 Kotlin 中将函数作为参数传递

我们可以将一个函数作为参数传递给另一个函数。这两个函数都可以自由地具有任何返回类型,并且可以具有任意数量的数据类型参数。

此外,参数函数可以没有参数和/或没有返回值。

示例 1:

让我们看一个具有两个函数的 Employee 类的示例,years 计算两个日期之间的年差,print_details 打印员工详细信息。

import java.time.LocalDate

// Employee class
public class Employee(id: Int, name: String, joining_date: LocalDate, leaving_date: LocalDate) {
    var name: String = name
    var id: Int = id
    var joining_date: LocalDate = joining_date
    var leaving_date: LocalDate = leaving_date
}

// function years returns difference between numbers of years of joining_date and leaving_date
fun years(date1 : LocalDate, date2 :LocalDate) : Int{
    return date1.getYear()-date2.getYear()
}

// printing employee detains and years served to the company by the employee
fun print_details(emp: Employee, years: (LocalDate, LocalDate) -> Int) {
    println(
        "Employee ID: ${emp.id}\n" +
                "Employee Name: ${emp.name}\n" +
                "Years served: ${years(emp.leaving_date, emp.joining_date)}"
    )
}

// main function
fun main(args: Array<String>) {

// object of Employee class
    var Raghav = Employee(1004, "Raghav Mishra", LocalDate.parse("2005-09-30"), LocalDate.parse("2012-11-17"))

    // printing detals
    print_details(Raghav, ::years)
}

输出:

Employee ID: 1004
Employee Name: Raghav Mishra
Years served: 7
  1. Employee 类对象有四个参数:nameidjoining_dateleaving_datejoining_dateleaving_date 的数据类型为 LocalDate
  2. year 函数采用两个 LocalDate 变量。getYear() 返回特定 LocalDate 中的年份,year 函数返回两个日期中年份的差为 Int
  3. print_details 采用 Employee 类的一个对象和一个计算员工服务年限的函数。它通过将 joining_dateleaving_date 传递给 years 函数来打印 Employee IDEmployee Name 和服务年限。
  4. 在 main 方法中,我们创建了一个 Employee 类的对象,并通过传递雇员 Raghav:: years 函数来调用 print_details 函数,该函数在输出中打印详细信息。
注意
:: 运算符(在此上下文中)创建对该函数的可调用引用。

示例 2:

为了理解和应用 Kotlin 的这个特性,让我们举一个更实际的例子来过滤列表。

fun main(args: Array<String>) {
    var nums = listOf(153, 534,773,894,247,52)
    fun odd(n : Int) : Boolean = n%2!=0
    println("Odd numbers in the list are:")
    nums.filter(::odd).forEach {n -> println(n)}
}

输出:

Odd numbers in the list are:
153
773
247

我们获取一个名为 nums 的数字列表。函数 oddInt 数作为参数,如果该数是奇数则返回 true

filter 函数将一个函数作为参数,该函数将 nums 的元素作为参数并返回一个布尔值。这里 numsInt 值的列表;因此,odd 必须将 Int 作为参数并返回一个布尔值。

odd 函数中导致 true 的所有元素都传递给(即过滤)forEach 函数。forEach() 函数将 Lambda 表达式或函数作为参数并返回一个 Unit,将每个数字打印到新行。

笔记:

nums.filter(::odd).forEach {n -> println(n)}

上面的代码也可以像下面这样编写以获得相同的结果。

nums.filter(::odd).forEach(::println)
//or
nums.filter{ odd(it) }.forEach { println(it) }
//or
nums.filter{odd(it)}.forEach(::println)

it 可以替换为其他名称,例如 Lambda 函数:nums.filter{n -> odd(n)}

因此总结一下: forEach{n -> println(n)} == forEach{println(it)} == forEach(::println)

Kotlin 函数作为返回值

高阶函数也是返回另一个函数的函数。再次返回的函数可以有一个返回值或返回一个 Unit 并且不带或不带任何数量的参数。

让我们看一个计算器的例子,我们让用户输入 2 个数字,让用户选择如何操作这些数字,最后返回结果。

我们将采用两个函数,第一个函数根据用户给出的选择返回一个函数。第二个函数取第一个函数返回的函数并运行。

// add func
fun add(a: Int, b: Int) : Int = a+b
// sub func
fun subtract(a: Int, b: Int) : Int = a-b
// multiply func
fun multiply(a: Int, b: Int) : Int {return a*b}
// division func
fun divide(a: Int, b: Int) : Int {return a/b}
// incorrect input
fun error(a: Int, b: Int) : Int {
    println("error");
    return -1
}

// choose returns a function requested by the user
fun choose(choice : Int) : (a : Int, b : Int)-> Int{
    return when(choice){
        1 -> ::add
        2 -> ::subtract
        3 -> ::multiply
        4 -> ::divide
        else -> ::error
    }
}

// function returning a function
fun perform(a : Int, b : Int, action : (a : Int, b : Int)-> Int) : Int{
    return action(a,b)
}

fun main(){
    println("Enter first number")
    var a = Integer.parseInt(readLine())
    println("Enter second number")
    var b = Integer.parseInt(readLine())
    println("Enter choice:\n1 add\n2 subtract\n3 multiply\n4 divide")
    var choice = Integer.parseInt(readLine())
    var result = perform(a,b,choose(choice))
    println("Result = $result")
}

输出:

Enter the first number
8493
Enter the second number
3
Enter choice:
1 add
2 subtract
3 multiply
4 divide
4
Result = 2831

在 main 函数中,我们先要求用户输入 2 个数字,然后再要求选择。在 choice 方法中,我们传递用户的选择。

choice 函数返回用户选择的函数。因此,我们创建了五个函数:addsubmultiplydivideerror

其中之一由 choice 函数返回。然后将该函数传递给 perform 函数,它需要两个 Int 类型的数字,以及一个由 choice 函数返回的函数,例如 func

两个用户输入的数字被传递给 funcfunc 返回的结果作为输出打印。

此外,请注意 addsub 使用 =(等于)符号初始化。multiplydivide{} 初始化。

两者都是正确的并且可以使用,但是 addsub 更容易理解。

在 Kotlin 中传递 Lambda 表达式而不是函数

在 Kotlin 中,如果我们必须只使用一次函数并且函数是单行表达式,我们可以传递 Lambda 表达式而不是函数。比如说,而不是传递 divide 函数;我们选择直接传递表达式来执行如下功能。

var res = perform(a,b, { a,b -> a/b })
作者: Niyati Thakkar
Niyati Thakkar avatar Niyati Thakkar avatar

Niyati is a Technical Content Writer and an engineering student. She has written more than 50 published articles on Data Structures, Algorithms, Git, DBMS, and Programming Languages like Python, C/C++, Java, CSS, HTML, KOTLIN, JavaScript, etc. that are very easy-to-understand and visualize.

LinkedIn

相关文章 - Kotlin Function