高阶函数
如果语言的函数可以接受其他函数作为参数并产生函数作为返回值,则称其支持高阶函数。
高阶函数是函数式编程语言的一个重要部分。在之前的原子中,我们已经看到了诸如 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 找到。