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