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