Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

高阶函数

如果语言的函数可以接受其他函数作为参数并产生函数作为返回值,则称其支持高阶函数

高阶函数是函数式编程语言的一个重要部分。在之前的原子中,我们已经看到了诸如 filter()map()any() 等高阶函数。

您可以将一个 Lambda 存储在引用中。让我们看看这种存储的类型:

// HigherOrderFunctions/IsPlus.kt
package higherorderfunctions
import atomictest.eq

val isPlus: (Int) -> Boolean = { it > 0 }

fun main() {
  listOf(1, 2, -3).any(isPlus) eq true
}

(Int) -> Boolean 是函数类型:它以括号开始,括号中包含零个或多个参数类型,然后是一个箭头 (->),后跟返回类型:

(参数1类型, 参数2类型... 参数N类型) -> 返回类型

通过引用调用函数的语法与普通函数调用相同:

// HigherOrderFunctions/CallingReference.kt
package higherorderfunctions
import atomictest.eq

val helloWorld: () -> String =
  { "Hello, world!" }

val sum: (Int, Int) -> Int =
  { x, y -> x + y }

fun main() {
  helloWorld() eq "Hello, world!"
  sum(1, 2) eq 3
}

当函数接受一个函数参数时,您可以传递给它一个函数引用或 Lambda。考虑如何定义标准库中的 any()

// HigherOrderFunctions/Any.kt
package higherorderfunctions
import atomictest.eq

fun <T> List<T>.any(                    // [1]
  predicate: (T) -> Boolean             // [2]
): Boolean {
  for (element in this) {
    if (predicate(element))             // [3]
      return true
  }
  return false
}

fun main() {
  val ints = listOf(1, 2, -3)
  ints.any { it > 0 } eq true           // [4]

  val strings = listOf("abc", " ")
  strings.any { it.isBlank() } eq true  // [5]
  strings.any(String::isNotBlank) eq    // [6]
    true
}
  • [1] any() 应该适用于不同类型的 List,因此我们将其定义为泛型 List<T> 的扩展函数。
  • [2] predicate 函数可以调用参数类型为 T 的参数,因此我们可以将其应用于 List 元素。
  • [3] 应用 predicate() 可以判断 element 是否符合我们的条件。
  • Lambda 的类型有所不同:在 [4] 中是 Int,在 [5] 中是 String
  • [6] 成员引用是传递函数引用的另一种方式。

标准库中的 repeat() 接受一个函数作为其第二个参数。它将一个操作重复多次:

// HigherOrderFunctions/RepeatByInt.kt
import atomictest.*

fun main() {
  repeat(4) { trace("hi!") }
  trace eq "hi! hi! hi! hi!"
}

考虑如何定义 repeat()

// HigherOrderFunctions/Repeat.kt
package higherorderfunctions
import atomictest.*

fun repeat(
  times: Int,
  action: (Int) -> Unit           // [1]
) {
  for (index in 0 until times) {
    action(index)                 // [2]
  }
}

fun main() {
  repeat(3) { trace("#$it") }     // [3]
  trace eq "#0 #1 #2"
}
  • [1] repeat() 接受类型为 (Int) -> Unit 的参数 action
  • [2] 调用 action() 时,它会传递当前重复的 index
  • [3] 在调用 repeat() 时,您可以在 Lambda 中使用 it 访问重复的 index

函数的返回类型可以为可空:

// HigherOrderFunctions/NullableReturn.kt
import atomictest.eq

fun main() {
  val transform: (String) -> Int? =
    { s: String -> s.toIntOrNull() }
  transform("112") eq 112
  transform("abc") eq null
  val x = listOf("112", "abc")
  x.mapNotNull(transform) eq "[112]"
  x.mapNotNull { it.toIntOrNull() } eq "[112]"
}

toIntOrNull() 可能返回 null,因此 transform() 接受一个 String 并返回一个可空的 Int?mapNotNull()List 中的每个元素转换为可空值,并从结果中删除所有 null。它的效果与先调用 map(),然后对所得列表应用 filterNotNull() 是相同的。

注意将返回类型可空与将整个函数类型可空之间的区别:

// HigherOrderFunctions/NullableFunction.kt
import atomictest.eq

fun main() {
  val returnTypeNullable: (String) -> Int? =
    { null }
  val mightBeNull: ((String) -> Int)? = null
  returnTypeNullable("abc") eq null
  // 无法在没有空检查的情况下编译:
  // mightBeNull("abc")
  if (mightBeNull != null) {
    mightBeNull("abc")
  }
}

在调用存储在 mightBeNull 中的函数之前,我们必须确保函数引用本身不为 null

练习和解答可在 www.AtomicKotlin.com 找到。