<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 高階函數與 lambda 表達式 [TOC] Kotlin 函數都是[*頭等的*](https://zh.wikipedia.org/wiki/%E5%A4%B4%E7%AD%89%E5%87%BD%E6%95%B0),這意味著它們可以存儲在變量與數據結構中、作為參數傳遞給其他[高階函數](http://www.kotlincn.net/docs/reference/lambdas.html#%E9%AB%98%E9%98%B6%E5%87%BD%E6%95%B0)以及從其他高階函數返回。可以像操作任何其他非函數值一樣操作函數。 為促成這點,作為一門靜態類型編程語言的 Kotlin 使用一系列[函數類型](http://www.kotlincn.net/docs/reference/lambdas.html#%E5%87%BD%E6%95%B0%E7%B1%BB%E5%9E%8B)來表示函數并提供一組特定的語言結構,例如 [lambda 表達式](http://www.kotlincn.net/docs/reference/lambdas.html#lambda-%E8%A1%A8%E8%BE%BE%E5%BC%8F%E4%B8%8E%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)。 ## 高階函數 高階函數是**將函數用作參數或返回值的函數**。一個不錯的示例是集合的[函數式風格的 `fold`](https://en.wikipedia.org/wiki/Fold_(higher-order_function)),它接受一個初始累積值與一個接合函數,并通過將當前累積值與每個集合元素連續接合起來代入累積值來構建返回值: ```kotlin fun <T, R> Collection<T>.fold( initial: R, combine: (acc: R, nextElement: T) -> R ): R { var accumulator: R = initial for (element: T in this) { accumulator = combine(accumulator, element) } return accumulator } ``` 在上述代碼中,參數 `combine` 具有[函數類型](http://www.kotlincn.net/docs/reference/lambdas.html#%E5%87%BD%E6%95%B0%E7%B1%BB%E5%9E%8B) `(R, T) -> R`,因此 `fold` 接受一個函數作為參數,該函數接受類型分別為 `R` 與 `T` 的兩個參數并返回一個 `R` 類型的值。在 *for*-循環內部調用該函數,然后將其返回值賦值給 `accumulator`。 為了調用 `fold`,需要傳給它一個[函數類型的實例](http://www.kotlincn.net/docs/reference/lambdas.html#%E5%87%BD%E6%95%B0%E7%B1%BB%E5%9E%8B%E5%AE%9E%E4%BE%8B%E5%8C%96)作為參數,而在高階函數調用處,([下文詳述的](http://www.kotlincn.net/docs/reference/lambdas.html#lambda-%E8%A1%A8%E8%BE%BE%E5%BC%8F%E4%B8%8E%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0))lambda 表達 式廣泛用于此目的。 ``` fun main() { //sampleStart val items = listOf(1, 2, 3, 4, 5) // Lambdas 表達式是花括號括起來的代碼塊。 items.fold(0, { // 如果一個 lambda 表達式有參數,前面是參數,后跟“->” acc: Int, i: Int -> print("acc = $acc, i = $i, ") val result = acc + i println("result = $result") // lambda 表達式中的最后一個表達式是返回值: result }) // lambda 表達式的參數類型是可選的,如果能夠推斷出來的話: val joinedToString = items.fold("Elements:", { acc, i -> acc + " " + i }) // 函數引用也可以用于高階函數調用: val product = items.fold(1, Int::times) //sampleEnd println("joinedToString = $joinedToString") println("product = $product") } ``` 執行結果 ``` acc = 0, i = 1, result = 1 acc = 1, i = 2, result = 3 acc = 3, i = 3, result = 6 acc = 6, i = 4, result = 10 acc = 10, i = 5, result = 15 joinedToString = Elements: 1 2 3 4 5 product = 120 ``` 以下各節會更詳細地解釋上文提到的這些概念。 ## 函數類型 Kotlin 使用類似 `(Int) -> String` 的一系列函數類型來處理函數的聲明: `val onClick: () -> Unit = ……`。 這些類型具有與函數簽名相對應的特殊表示法,即它們的參數和返回值: * 所有函數類型都有一個圓括號括起來的參數類型列表以及一個返回類型:`(A, B) -> C` 表示接受類型分別為 `A` 與 `B` 兩個參數并返回一個 `C` 類型值的函數類型。參數類型列表可以為空,如 `() -> A`。[`Unit` 返回類型](http://www.kotlincn.net/docs/reference/functions.html#%E8%BF%94%E5%9B%9E-unit-%E7%9A%84%E5%87%BD%E6%95%B0)不可省略。 * 函數類型可以有一個額外的*接收者*類型,它在表示法中的點之前指定:類型 `A.(B) -> C` 表示可以在 `A` 的接收者對象上以一個 `B` 類型參數來調用并返回一個 `C` 類型值的函數。 [帶有接收者的函數字面值](http://www.kotlincn.net/docs/reference/lambdas.html#%E5%B8%A6%E6%9C%89%E6%8E%A5%E6%94%B6%E8%80%85%E7%9A%84%E5%87%BD%E6%95%B0%E5%AD%97%E9%9D%A2%E5%80%BC)通常與這些類型一起使用。 * [掛起函數](http://www.kotlincn.net/docs/reference/coroutines.html#%E6%8C%82%E8%B5%B7%E5%87%BD%E6%95%B0)屬于特殊種類的函數類型,它的表示法中有一個 *suspend*{: .keyword} 修飾符 ,例如 `suspend () -> Unit` 或者 `suspend A.(B) -> C`。 函數類型表示法可以選擇性地包含函數的參數名:`(x: Int, y: Int) -> Point`。這些名稱可用于表明參數的含義。 > 如需將函數類型指定為[可空](http://www.kotlincn.net/docs/reference/null-safety.html#%E5%8F%AF%E7%A9%BA%E7%B1%BB%E5%9E%8B%E4%B8%8E%E9%9D%9E%E7%A9%BA%E7%B1%BB%E5%9E%8B),請使用圓括號:`((Int, Int) -> Int)?`。 > 函數類型可以使用圓括號進行接合:`(Int) -> ((Int) -> Unit)` > 箭頭表示法是右結合的,`(Int) -> (Int) -> Unit` 與前述示例等價,但不等于`((Int) -> (Int)) -> Unit`。 還可以通過使用[類型別名](http://www.kotlincn.net/docs/reference/type-aliases.html)給函數類型起一個別稱: ```kotlin typealias ClickHandler = (Button, ClickEvent) -> Unit ``` ### 函數類型實例化 有幾種方法可以獲得函數類型的實例: * 使用函數字面值的代碼塊,采用以下形式之一: * [lambda 表達式](http://www.kotlincn.net/docs/reference/lambdas.html#lambda-%E8%A1%A8%E8%BE%BE%E5%BC%8F%E4%B8%8E%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0): `{ a, b -> a + b }`, * [匿名函數](http://www.kotlincn.net/docs/reference/lambdas.html#%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0): `fun(s: String): Int { return s.toIntOrNull() ?: 0 }` [帶有接收者的函數字面值](http://www.kotlincn.net/docs/reference/lambdas.html#%E5%B8%A6%E6%9C%89%E6%8E%A5%E6%94%B6%E8%80%85%E7%9A%84%E5%87%BD%E6%95%B0%E5%AD%97%E9%9D%A2%E5%80%BC)可用作帶有接收者的函數類型的值。 * 使用已有聲明的[可調用引用](http://www.kotlincn.net/docs/reference/reflection.html#%E5%8F%AF%E8%B0%83%E7%94%A8%E5%BC%95%E7%94%A8):(函數引用,屬性引用等) * 頂層、局部、成員、擴展[函數](http://www.kotlincn.net/docs/reference/reflection.html#%E5%87%BD%E6%95%B0%E5%BC%95%E7%94%A8):`::isOdd`、 `String::toInt`, * 頂層、成員、擴展[屬性](http://www.kotlincn.net/docs/reference/reflection.html#%E5%B1%9E%E6%80%A7%E5%BC%95%E7%94%A8):`List<Int>::size`, * [構造函數](http://www.kotlincn.net/docs/reference/reflection.html#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E5%BC%95%E7%94%A8):`::Regex` 這包括指向特定實例成員的[綁定的可調用引用](http://www.kotlincn.net/docs/reference/reflection.html#%E7%BB%91%E5%AE%9A%E7%9A%84%E5%87%BD%E6%95%B0%E4%B8%8E%E5%B1%9E%E6%80%A7%E5%BC%95%E7%94%A8%E8%87%AA-11-%E8%B5%B7):`foo::toString`。 * 使用實現函數類型接口的自定義類的實例: ```kotlin class IntTransformer: (Int) -> Int { override operator fun invoke(x: Int): Int = TODO() } val intFunction: (Int) -> Int = IntTransformer() ``` 如果有足夠信息,編譯器可以推斷變量的函數類型: ```kotlin val a = { i: Int -> i + 1 } // 推斷出的類型是 (Int) -> Int ``` 帶與不帶接收者的函數類型*非字面*值可以互換,其中接收者可以替代第一個參數,反之亦然。例如,`(A, B) -> C` 類型的值可以傳給或賦值給期待 `A.(B) -> C` 的地方,反之亦然: ```kotlin fun main() { val repeatFun: String.(Int) -> String = { times -> this.repeat(times) } val twoParameters: (String, Int) -> String = repeatFun // OK fun runTransformation(f: (String, Int) -> String): String { return f("hello", 3) } val result = runTransformation(repeatFun) // OK println("result = $result")//result = hellohellohello } ``` > 請注意,默認情況下推斷出的是沒有接收者的函數類型,即使變量是通過擴展函數引用來初始化的。如需改變這點,請顯式指定變量類型。 ### 函數類型實例調用 函數類型的值可以通過其 [`invoke(……)` 操作符](http://www.kotlincn.net/docs/reference/operator-overloading.html#invoke)調用:`f.invoke(x)` 或者直接 `f(x)`。 如果該值具有接收者類型,那么應該將接收者對象作為第一個參數傳遞。調用帶有接收者的函數類型值的另一個方式是在其前面加上接收者對象,就好比該值是一個[擴展函數](http://www.kotlincn.net/docs/reference/extensions.html):`1.foo(2)`, 例如: ```kotlin fun main() { val stringPlus: (String, String) -> String = String::plus val intPlus: Int.(Int) -> Int = Int::plus println(stringPlus.invoke("<-", "->"))//<--> println(stringPlus("Hello, ", "world!")) //Hello, world! println(intPlus.invoke(1, 1))//2 println(intPlus(1, 2))//3 // 類擴展調用 println(2.intPlus(3)) //5 } ``` ### 內聯函數 有時使用[內聯函數](http://www.kotlincn.net/docs/reference/inline-functions.html)可以為高階函數提供靈活的控制流。 ## Lambda 表達式與匿名函數 lambda 表達式與匿名函數是“函數字面值”,即未聲明的函數,但立即做為表達式傳遞。考慮下面的例子: ```kotlin max(strings, { a, b -> a.length < b.length }) ``` 函數 `max` 是一個高階函數,它接受一個函數作為第二個參數。其第二個參數是一個表達式,它本身是一個函數,即函數字面值,它等價于以下命名函數: ```kotlin fun compare(a: String, b: String): Boolean = a.length < b.length ``` ### Lambda 表達式語法 Lambda 表達式的完整語法形式如下: ```kotlin val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y } ``` **lambda 表達式總是括在花括號中**,完整語法形式的參數聲明放在花括號內,并有可選的類型標注,函數體(比如` x + y`)跟在一個 `->` 符號之后。如果推斷出的該 lambda 的返回類型不是 `Unit`,那么該 lambda 主體中的最后一個(或可能是單個)表達式會視為返回值。 如果我們把所有可選標注都留下,看起來如下: ```kotlin val sum = { x, y -> x + y } ``` ### 將 lambda 表達式傳給最后一個參數 在 Kotlin 中有一個約定:**如果函數的最后一個參數是函數,那么作為相應參數傳入的 lambda 表達式可以放在圓括號之外**: ```kotlin val product = items.fold(1) { acc, e -> acc * e } ``` 這種語法也稱為**拖尾 lambda 表達式**。如果該 lambda 表達式是調用時唯一的參數,那么圓括號可以完全省略: ```kotlin run { println("...") } ``` ### `it`:單個參數的隱式名稱 一個 lambda 表達式只有一個參數也是很常見的。 **如果編譯器自己可以識別出簽名,也可以不用聲明唯一的參數并忽略 `->`。該參數會隱式聲明為 `it`**: ```kotlin ints.filter { it > 0 } // 這個字面值是“(it: Int) -> Boolean”類型的 ``` ### 從 lambda 表達式中返回一個值 我們可以使用[限定的返回](http://www.kotlincn.net/docs/reference/returns.html#%E6%A0%87%E7%AD%BE%E5%A4%84%E8%BF%94%E5%9B%9E)語法(標簽處返回)從 lambda 顯式返回一個值。否則,將隱式返回最后一個表達式的值。因此,以下兩個片段是等價的: ```kotlin ints.filter { val shouldFilter = it > 0 shouldFilter } ints.filter { val shouldFilter = it > 0 return@filter shouldFilter } ``` 這一約定連同[在圓括號外傳遞 lambda 表達式](#將-lambda-表達式傳給最后一個參數)一起支持[LINQ-風格](http://msdn.microsoft.com/en-us/library/bb308959.aspx) 的代碼: ```kotlin strings.filter { it.length == 5 }.sortedBy { it }.map { it.toUpperCase() } ``` ### 下劃線用于未使用的變量(自 1.1 起) 如果 lambda 表達式的參數未使用,那么可以用下劃線取代其名稱: ``` map.forEach { _, value -> println("$value!") } ``` ### 在 lambda 表達式中解構(自 1.1 起) 在 lambda 表達式中解構是作為[解構聲明](http://www.kotlincn.net/docs/reference/multi-declarations.html#%E5%9C%A8-lambda-%E8%A1%A8%E8%BE%BE%E5%BC%8F%E4%B8%AD%E8%A7%A3%E6%9E%84%E8%87%AA-11-%E8%B5%B7)的一部分描述的。 ### 匿名函數 上面提供的 lambda 表達式語法缺少的一個東西是指定函數的返回類型的能力。**在大多數情況下,這是不必要的。因為返回類型可以自動推斷出來**。然而,如果確實需要顯式指定,可以使用另一種語法:**匿名函數**。 ```kotlin fun(x: Int, y: Int): Int = x + y ``` 匿名函數看起來非常像一個常規函數聲明,除了其名稱省略了。其函數體可以是表達式(如上所示)或代碼塊: ```kotlin fun(x: Int, y: Int): Int { return x + y } ``` 參數和返回類型的指定方式與常規函數相同,除了能夠從上下文推斷出的參數類型可以省略: ```kotlin ints.filter(fun(item) = item > 0) ``` 匿名函數的返回類型推斷機制與正常函數一樣:對于具有表達式函數體的匿名函數將自動推斷返回類型,而具有代碼塊函數體的返回類型必須顯式指定(或者已假定為 `Unit`)。 請注意,匿名函數參數總是在括號內傳遞。 允許將函數留在圓括號外的簡寫語法僅適用于 lambda 表達式。 Lambda表達式與匿名函數之間的另一個區別是[非局部返回](http://www.kotlincn.net/docs/reference/inline-functions.html#%E9%9D%9E%E5%B1%80%E9%83%A8%E8%BF%94%E5%9B%9E)的行為。一個不帶標簽的 *return*語句總是在用 *fun* 關鍵字聲明的函數中返回。這意味著 lambda 表達式中的 *return*將從包含它的函數返回,而匿名函數中的 *return*將從匿名函數自身返回。 ### 閉包 Lambda 表達式或者匿名函數(以及[局部函數](http://www.kotlincn.net/docs/reference/functions.html#%E5%B1%80%E9%83%A8%E5%87%BD%E6%95%B0)和[對象表達式](http://www.kotlincn.net/docs/reference/object-declarations.html#%E5%AF%B9%E8%B1%A1%E8%A1%A8%E8%BE%BE%E5%BC%8F)) 可以訪問其 _閉包_ ,即在外部作用域中聲明的變量。 在 lambda 表達式中可以修改閉包中捕獲的變量: ```kotlin var sum = 0 ints.filter { it > 0 }.forEach { sum += it } print(sum) ``` ### 帶有接收者的函數字面值 帶有接收者的[函數類型](http://www.kotlincn.net/docs/reference/lambdas.html#%E5%87%BD%E6%95%B0%E7%B1%BB%E5%9E%8B),例如 `A.(B) -> C`,可以用特殊形式的函數字面值實例化—— 帶有接收者的函數字面值。 如上所述,Kotlin 提供了[調用](http://www.kotlincn.net/docs/reference/lambdas.html#%E5%87%BD%E6%95%B0%E7%B1%BB%E5%9E%8B%E5%AE%9E%E4%BE%8B%E8%B0%83%E7%94%A8)帶有接收者(提供*接收者對象*)的函數類型實例的能力。 在這樣的函數字面值內部,傳給調用的接收者對象成為*隱式*的*this*,以便訪問接收者對象的成員而無需任何額外的限定符,亦可使用[`this` 表達式](http://www.kotlincn.net/docs/reference/this-expressions.html) 訪問接收者對象。 這種行為與[擴展函數](http://www.kotlincn.net/docs/reference/extensions.html)類似,擴展函數也允許在函數體內部訪問接收者對象的成員。 這里有一個帶有接收者的函數字面值及其類型的示例,其中在接收者對象上調用了 `plus`: ```kotlin val sum: Int.(Int) -> Int = { other -> plus(other) } ``` 匿名函數語法允許你直接指定函數字面值的接收者類型。如果你需要使用帶接收者的函數類型聲明一個變量,并在之后使用它,這將非常有用。 ```kotlin val sum = fun Int.(other: Int): Int = this + other ``` 當接收者類型可以從上下文推斷時,lambda 表達式可以用作帶接收者的函數字面值。 One of the most important examples of their usage is [type-safe builders](http://www.kotlincn.net/docs/reference/type-safe-builders.html): ```kotlin class HTML { fun body() { …… } } fun html(init: HTML.() -> Unit): HTML { val html = HTML() // 創建接收者對象 html.init() // 將該接收者對象傳給該 lambda return html } html { // 帶接收者的 lambda 由此開始 body() // 調用該接收者對象的一個方法 } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看