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!
解释:
-
首先,我们在主函数中有另外两个函数,
hello
和random
。 -
hello()
不带参数并且不返回任何值(即返回Unit
)。 -
random()
采用一个参数,即在random
的范围内本地称为func
的函数,并且不返回任何值。func()
函数不接受任何参数并返回一个Unit
,这意味着它不返回值。因此,
func
是在调用random
时作为参数传递给函数的本地名称,:
(冒号)通常写在变量名称之后,以区分行为和变量名称,在()
func
函数所需的括号参数被写入,并且在->
之后需要函数的返回类型。 -
最后,我们将
hello
函数传递给 main 方法中的random
。
让我们看看输出以了解程序的流程。
首先,当我们从将 hello
作为参数传递的 main
函数中调用 random
时,我们在 random
函数内部。因此,我们从随机打印的上述两行。
现在 random 称为 func
,它只是 hello
函数,因为我们将它作为参数传递,因此在最后一行,它在 hello
函数中。
Kotlin 中的 Lambda 表达式
Lambda 表达式是函数文字或匿名函数,我们可以在其中以简短易读的方式编写函数。它们还使代码看起来紧凑而干净。
例如:
val sum = {num1: Int , num2: Int -> num1 + num2}
sum
采用两个 Int
变量,num1
和 num2
,并返回一个 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
Employee
类对象有四个参数:name
、id
、joining_date
和leaving_date
。joining_date
和leaving_date
的数据类型为LocalDate
。year
函数采用两个LocalDate
变量。getYear()
返回特定LocalDate
中的年份,year
函数返回两个日期中年份的差为Int
。print_details
采用Employee
类的一个对象和一个计算员工服务年限的函数。它通过将joining_date
和leaving_date
传递给years
函数来打印Employee ID
、Employee Name
和服务年限。- 在 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
的数字列表。函数 odd
将 Int
数作为参数,如果该数是奇数则返回 true
。
filter
函数将一个函数作为参数,该函数将 nums
的元素作为参数并返回一个布尔值。这里 nums
是 Int
值的列表;因此,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
函数返回用户选择的函数。因此,我们创建了五个函数:add
、sub
、multiply
、divide
和 error
。
其中之一由 choice
函数返回。然后将该函数传递给 perform
函数,它需要两个 Int
类型的数字,以及一个由 choice
函数返回的函数,例如 func
。
两个用户输入的数字被传递给 func
,func
返回的结果作为输出打印。
此外,请注意 add
和 sub
使用 =
(等于)符号初始化。multiply
和 divide
用 {}
初始化。
两者都是正确的并且可以使用,但是 add
和 sub
更容易理解。
在 Kotlin 中传递 Lambda 表达式而不是函数
在 Kotlin 中,如果我们必须只使用一次函数并且函数是单行表达式,我们可以传递 Lambda 表达式而不是函数。比如说,而不是传递 divide
函数;我们选择直接传递表达式来执行如下功能。
var res = perform(a,b, { a,b -> a/b })
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