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