# 高阶函数与 Lambda
## 高阶函数
所谓高阶函数就是有函数作为参数的函数,或是返回一个函数。这种函数的一个好的例子就是 `lock()`,带有一个 lock 对象和一个函数,获得 lock,运行函数然后释放这个 lock:
``` kotlin
fun <T> lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
```
让我们审视一下上面的代码:`body` 有一个[函数类型](#function-types) `() -> T`,所以假定它是一个函数不带参数并返回一个类型为 `T` 的值,它在 `try` 块中被调用,又被 `lock` 所保护,而且它的结果通过 `lock()` 函数返回。
如果我们要调用 `lock()`,我们可以传递不同的函数作为实参给它(查看[函数引用](reflection.html#function-references)):
``` kotlin
fun toBeSynchronized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchronized)
```
另外,一般更方便的方式是传递一个 [lambda 表达式](#lambda-expressions-and-anonymous-functions):
``` kotlin
val result = lock(lock, { sharedResource.operation() })
```
lambda 表达式在下面有更[详细的描述](#lambda-expressions-and-anonymous-functions),但为了本节内容的连续性,让我们概览一下:
* 一个 lambda 表达式始终围绕着花括号,
* 它的参数(如果有的话)在 `->` 的前面声明(参数的类型可以被推断出),
* 主体在 `->` 后面(如果有的话)
在 Kotlin 里面,有一个约定,如果最后那个函数参数是一个函数,那么那个函数可以在圆括号外面指定:
``` kotlin
lock (lock) {
sharedResource.operation()
}
```
另一个高阶函数的例子应该是 `map()`:
``` kotlin
fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
val result = arrayListOf<R>()
for (item in this)
result.add(transform(item))
return result
}
```
这个函数可以在下面调用:
``` kotlin
val doubled = ints.map { it -> it * 2 }
```
注意如果 lambda 是调用时仅有的实参,那么圆括号在调用时完全可以省略。
另一个很有帮助的约定是如果一个函数字面上只有一个参数,它的声明可以省略(`->` 顺道省略),并且它的名称将是 `it`:
``` kotlin
ints.map { it * 2 }
```
这些约定允许书写 [LINQ 风格](http://msdn.microsoft.com/en-us/library/bb308959.aspx)的代码:
``` kotlin
strings.filter { it.length == 5 }.sortBy { it }.map { it.toUpperCase() }
```
## 内联函数
有时使用[内联函数](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()
```
### 函数类型
要一个函数作为参数接受另一个函数,我们得为那个参数指定一个函数类型。例如上述的函数 `max` 定义如下:
``` kotlin
fun <T> max(collection: Collection<T>, less: (T, T) -> Boolean): T? {
var max: T? = null
for (it in collection)
if (max == null || less(max, it))
max = it
return max
}
```
参数 `less` 的类型是 `(T, T) -> Boolean`,意即一个函数带有两个类型为 `T` 的参数并返回一个 `Boolean`:如果第一个比第二个小则为真。
主体第 4 行,`less` 作为一个函数使用:它通过传递两个类型为 `T` 的实参来调用。
一个函数类型要像上面那样书写,或者如果你要记录每个参数的意义的话,也可以有命名的参数。
``` kotlin
val compare: (x: T, y: T) -> Int = ...
```
### lambda 表达式语法
lambda 表达式完整的语法形式,即函数类型的文本,是像下面这样的:
``` kotlin
val sum = { x: Int, y: Int -> x + y }
```
lambda 表达式始终以花括号包围,完整语法形式中参数声明在圆括号里面并拥有可选的注解,主体则在一个 `->` 符号后面。如果可选的注解我们全部不要,看上去就是这样的:
``` kotlin
val sum: (Int, Int) -> Int = { x, y -> x + y }
```
lambda 表达式通常只有一个参数。如果 Kotlin 能推测它自身的签名,就能允许我们不声明这个仅有的参数,而是隐含地为我们把声明它为 `it`:
``` kotlin
ints.filter { it > 0 } // 字面类型为 '(it: Int) -> Boolean'
```
注意如果一个函数带了其它函数作为最后一个参数,那么这个 lambda 表达式实参可以在参数列表的括号外面传递。查看语法入门的[调用后缀](grammar.html#call-suffix)。
### 匿名函数
上面 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 表达式和匿名函数的是[非本地返回](inline-functions.html#non-local-returns)的行为。一个不带标签的 `return` 指令总是从 `func` 关键字声明的地方返回。这个意思就是一个 lambda 表达式里的 `return` 将从函数闭包返回,而匿名 函数的 `return` 将从这个匿名函数自身返回。
### 闭包
一个 lambda 表达式或匿名函数(与[局部函数](functions.html#local-functions)和[对象表达式](object-declarations.html#object-expressions)一样)可以访问它的_闭包_,意即这个在外部作用域声明的变量。与 Java 不同,这个在闭包内捕获的变量可以被修改:
``` kotlin
var sum = 0
ints.filter { it > 0 }.forEach {
sum += it
}
print(sum)
```
### 带接受者的函数字面量
Kotlin 提供了调用指定了_接受者对象_的函数字面量的功能。在函数字面量的主体中,你可以在接受者对象上调用方法而无须什么附加的限定符。这与扩展函数类似,允许你在函数主体内部访问接受者对象的成员。它们使用的一个很重要的例子是[类型安全的 Groovy 风格构建器](type-safe-builders.html)
这样的函数字面量的类型为一个带有接受者的函数类型:
``` kotlin
sum : Int.(other: Int) -> Int
```
这个函数字面量可以在接受者对象上像方法一样被调用:
``` kotlin
1.sum(2)
```
匿名函数语法允许你直接指定一个函数字面量的接受者类型。如果你需要声明一个带有接受者的函数类型的变量并在以后使用它,这会很有用。
``` kotlin
val sum = fun Int.(other: Int): Int = this + other
```
在接受者类型能够从上下文推断出来时,lambda 表达式可以像带接受者的函数字面量一样使用。
``` kotlin
class HTML {
fun body() { ... }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // 创建接受者对象
html.init() // 传递接受者对象到 lambda
return html
}
html { // 带接受者的 lambda
body() // 在接受者对象上调用一个方法
}
```