Higher-Order Functions and Lambdas1
Higher-Order Functions
A higher-order function is a function that takes functions as parameters, or returns a function.
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
参数,其类型为(R,T)->T
;
调用上述方法,使用lambda表达式传入combine
参数:
val items = listOf(1, 2, 3, 4, 5)
// Lambdas are code blocks enclosed in curly braces.
items.fold(0, {
// When a lambda has parameters, they go first, followed by '->'
acc: Int, i: Int ->
print("acc = $acc, i = $i, ")
val result = acc + i
println("result = $result")
// The last expression in a lambda is considered the return value:
result
})
// Parameter types in a lambda are optional if they can be inferred:
val joinedToString = items.fold("Elements:", { acc, i -> acc + " " + i })
// Function references can also be used for higher-order function calls:
val product = items.fold(1, Int::times)
运行结果如下:
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
Function types
eg.(Int) -> String
val onClick: () -> Unit = ...
声明函数类型时可以指定参数名: (x: Int, y: Int) -> Point
. 如x
,y
You can also give a function type an alternative name by using a type alias:
Instantiating a function type
There are several ways to obtain an instance of a function type:
-
Using a code block within a function literal, in one of the forms:
- a lambda expression: { a, b -> a + b },
- an anonymous function: fun(s: String): Int { return s.toIntOrNull() ?: 0 }
Function literals with receiver can be used as values of function types with receiver.
-
Using a callable reference to an existing declaration:
- a top-level, local, member, or extension function: ::isOdd, String::toInt,
- a top-level, member, or extension property: List::size,
- a constructor: ::Regex
These include bound callable references that point to a member of a particular instance: foo::toString.
-
Using instances of a custom class that implements a function type as an interface:
class IntTransformer: (Int) -> Int {
override operator fun invoke(x: Int): Int = TODO()
}
val intFunction: (Int) -> Int = IntTransformer()
Non-literal values of function types with and without receiver are interchangeable, so that the receiver can stand in for the first parameter, and vice versa. For instance, a value of type (A, B) -> C
can be passed or assigned where a A.(B) -> C
is expected and the other way around:
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
Invoking a function type instance
A value of a function type can be invoked by using its invoke(…) operator: f.invoke(x)
or just f(x)
.
If the value has a receiver type, the receiver object should be passed as the first argument. Another way to invoke a value of a function type with receiver is to prepend it with the receiver object, as if the value were an extension function: 1.foo(2)
,
val stringPlus: (String, String) -> String = String::plus
val intPlus: Int.(Int) -> Int = Int::plus
println(stringPlus.invoke("<-", "->"))
println(stringPlus("Hello, ", "world!"))
println(intPlus.invoke(1, 1))
println(intPlus(1, 2))
println(2.intPlus(3)) // extension-like call