《Kotin 极简教程》第8章 函数式编程（FP）(2)

1
1
0
1. 云栖社区>
2. 博客>
3. 正文

# 《Kotlin极简教程》正式上架：

## 8.2 在Kotlin中使用函数式编程

### 8.2.1 Kotlin中的函数

#### 函数声明

Kotlin 中的函数使用 fun 关键字声明

``````fun double(x: Int): Int {
return 2*x
}
``````

#### 函数用法

``````fun test() {
val doubleTwo = double(2)
println("double(2) = \$doubleTwo")
}
``````

``````object FPBasics {

fun double(x: Int): Int {
return 2 * x
}

fun test() {
val doubleTwo = double(2)
println("double(2) = \$doubleTwo")
}
}

fun main(args: Array<String>) {
FPBasics.test()
}
``````

### 8.2.2 扩展函数

``````    fun String.swap(index1: Int, index2: Int): String {
val charArray = this.toCharArray()
val tmp = charArray[index1]
charArray[index1] = charArray[index2]
charArray[index2] = tmp

return charArrayToString(charArray)
}

fun charArrayToString(charArray: CharArray): String {
var result = ""
charArray.forEach { it -> result = result + it }
return result
}

``````

``````        val str = "abcd"
val swapStr = str.swap(0, str.lastIndex)
println("str.swap(0, str.lastIndex) = \$swapStr")
``````

### 8.2.3 中缀函数

• 成员函数或扩展函数
• 只有一个参数
• `infix` 关键字标注

``````infix fun Int.shl(x: Int): Int {
...
}
``````

``````1 shl 2
``````

``````1.shl(2)
``````

### 8.2.4 函数参数

``````    fun powerOf(number: Int, exponent: Int): Int {
return Math.pow(number.toDouble(), exponent.toDouble()).toInt()
}
``````

``````        val eight = powerOf(2, 3)
println("powerOf(2,3) = \$eight")

``````

#### 默认参数

``````    fun add(x: Int = 0, y: Int = 0): Int {
return x + y
}
``````

``````        val zero = add()

``````

``````open class DefaultParamBase {
open fun add(x: Int = 0, y: Int = 0): Int {
return x + y
}
}

class DefaultParam : DefaultParamBase() {
override fun add(x: Int, y: Int): Int { // 不能有默认值
}
}
``````

#### 命名参数

``````    fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {

}
``````

``````reformat(str)
``````

``````reformat(str, true, true, false, '_')
``````

``````reformat(str,
normalizeCase = true,
upperCaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = '_'
)
``````

``````reformat(str, wordSeparator = '_')
``````

#### 可变数量的参数（Varargs）

``````fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
return result
}
``````

``````val list = asList(1, 2, 3)
``````

### 8.2.5 函数返回类型

#### 函数返回类型需要显式声明

Kotlin 不推断具有块代码体的函数的返回类型，因为这样的函数在代码体中可能有复杂的控制流，并且返回类型对于读者（有时对于编译器）也是不明显的。

#### 返回 Unit 的函数

``````    fun printHello(name: String?): Unit {
if (name != null)
println("Hello \${name}")
else
println("Hi there!")
// `return Unit` 或者 `return` 是可选的
}
``````

`Unit` 返回类型声明也是可选的。上面的代码等同于

``````fun printHello(name: String?) {
.....
}
``````

### 8.2.6 单表达式函数

``````fun double(x: Int): Int = x * 2
``````

``````fun double(x: Int) = x * 2
``````

### 8.2.7 函数作用域

#### 局部函数（嵌套函数）

Kotlin 支持局部函数，即一个函数在另一个函数内部

``````     fun sum(x: Int, y: Int, z: Int): Int {
val delta = 0;
fun add(a: Int, b: Int): Int {
return a + b + delta
}
}
``````

``````println("sum(1,2,3) = \${sum(0, 1, 2, 3)}")
``````

sum(1,2,3) = 6

#### 成员函数

``````class Sample() {
fun foo() { print("Foo") }
}
``````

``````Sample().foo() // 创建类 Sample 实例并调用 foo
``````

### 8.2.8 泛型函数

``````public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}
``````

### 8.2.9 高阶函数

``````public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
``````

``````(X)->Y
``````

``````    fun isOdd(x: Int): Boolean {
return x % 2 == 1
}

val list = listOf(1, 2, 3, 4, 5)
list.filter(::isOdd)

``````

### 8.2.10 匿名函数

``````        list.filter((fun(x: Int): Boolean {
return x % 2 == 1
}))
``````

### 8.2.11 Lambda 表达式

``````        list.filter {
it % 2 == 1
}
``````
• lambda 表达式总是被大括号 `{}` 括着
• 其参数（如果有的话）在 `->` 之前声明（参数类型可以省略）
• 函数体（如果存在的话）在 `->` 后面

``````        list.filter({
it % 2 == 1
})
``````

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

``````

``````>>> val sum = {x:Int ->  {y:Int -> x+y }}
>>> sum
(kotlin.Int) -> (kotlin.Int) -> kotlin.Int
>>> sum(1)(1)
2
``````

### 8.2.11 `it`：单个参数的隐式名称

Kotlin中另一个有用的约定是，如果函数字面值只有一个参数，

``````>>> val list = listOf(1,2,3,4,5)
>>> list.map { it * 2 }
[2, 4, 6, 8, 10]
``````

### 8.2.12 闭包（Closure）

Lambda 表达式或者匿名函数，以及局部函数和对象表达式（object declarations）可以访问其 闭包 ，即在外部作用域中声明的变量。 与 Java 不同的是可以修改闭包中捕获的变量：

``````    fun sumGTZero(c: Iterable<Int>): Int {
var sum = 0
c.filter { it > 0 }.forEach {
sum += it
}
return sum
}

val list = listOf(1, 2, 3, 4, 5)
sumGTZero(list) // 输出 15
``````

``````    fun closureDemo() {
for (i in 1..10) {
println("I = \$i")
}

}).start()

for (j in 10..20) {
println("J = \$j")
}
}).start()
}
``````

``````I = 1
J = 10
I = 2
I = 3
...
J = 20
``````

### 8.2.13 带接收者的函数字面值

Kotlin 提供了使用指定的 接收者对象 调用函数字面值的功能。

``````>>> val sum = fun Int.(other: Int): Int = this + other
>>> 1.sum(1)
2
``````

``````class HTML {
fun body() {
println("HTML BODY")
}
}

fun html(init: HTML.() -> Unit): HTML { // HTML.()中的HTML是接受者类型
val html = HTML()  // 创建接收者对象
html.init()        // 将该接收者对象传给该 lambda
return html
}
``````

``````    html {
body()
}

``````

### 8.2.14 具体化的类型参数

``````fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
while (p != null && !clazz.isInstance(p)) {
p = p.parent
}
@Suppress("UNCHECKED_CAST")
return p as T?
}
``````

``````treeNode.findParentOfType(MyTreeNode::class.java)
``````

``````treeNode.findParentOfType<MyTreeNode>()
``````

``````inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
``````

``````inline fun <reified T> membersOf() = T::class.members

fun main(s: Array<String>) {
println(membersOf<StringBuilder>().joinToString("\n"))
}
``````

### 8.2.10 尾递归tailrec

Kotlin 支持一种称为尾递归的函数式编程风格。 这允许一些通常用循环写的算法改用递归函数来写，而无堆栈溢出的风险。 当一个函数用 tailrec 修饰符标记并满足所需的形式时，编译器会优化该递归，生成一个快速而高效的基于循环的版本。

``````tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x)) // 函数必须将其自身调用作为它执行的最后一个操作

``````

``````private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (x == y) return y
x = y
}
}
``````

Kotlin 还为集合类引入了许多扩展函数。例如，使用 map() 和 filter() 函数可以流畅地操纵数据，具体的函数的使用以及示例我们已经在 集合类 章节中介绍。

+ 关注