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