折叠列表
fold()
将列表中的所有元素依次组合在一起,生成单个结果。
一个常见的练习是使用 fold()
实现诸如 sum()
或 reverse()
之类的操作。在这里,fold()
对序列求和:
// FoldingLists/SumViaFold.kt
import atomictest.eq
fun main() {
val list = listOf(1, 10, 100, 1000)
list.fold(0) { sum, n ->
sum + n
} eq 1111
}
fold()
接受初始值(在这种情况下是参数 0
),并逐个将操作(在这里表示为 lambda)应用于将当前累积值与每个元素组合在一起。fold()
首先将 0
(初始值)和 1
相加,得到 1
。这成为了 sum
,然后将其与 10
相加,得到 11
,这成为了新的 sum
。该操作对两个其他元素(100
和 1000
)重复执行。这产生了 111
和 1111
。当列表中没有其他内容时,fold()
将停止,并返回最终的 sum
,即 1111
。当然,fold()
实际上并不知道它正在执行“求和”操作,我们选择了标识符名称,以使理解更容易。
为了阐明 fold()
中的步骤,以下是使用普通的 for
循环编写的 SumViaFold.kt
:
// FoldingLists/FoldVsForLoop.kt
import atomictest.eq
fun main() {
val list = listOf(1, 10, 100, 1000)
var accumulator = 0
val operation =
{ sum: Int, i: Int -> sum + i }
for (i in list) {
accumulator = operation(accumulator, i)
}
accumulator eq 1111
}
fold()
通过逐个将 operation
应用于将当前元素与累积值组合在一起来累积值。
尽管 fold()
是一个重要的概念,也是在纯函数式语言中累积值的唯一方法,但在 Kotlin 中有时仍然会使用普通的 for
循环。
foldRight()
从右到左处理元素,与从左到右处理元素的 fold()
相反。以下示例演示了这种差异:
// FoldingLists/FoldRight.kt
import atomictest.eq
fun main() {
val list = listOf('a', 'b', 'c', 'd')
list.fold("*") { acc, elem ->
"($acc) + $elem"
} eq "((((*) + a) + b) + c) + d"
list.foldRight("*") { elem, acc ->
"$elem + ($acc)"
} eq "a + (b + (c + (d + (*))))"
}
fold()
首先将操作应用于 a
,如 (*) + a
所示,而 foldRight()
首先处理右侧的元素 d
,然后最后处理 a
。
fold()
和 foldRight()
的第一个参数是显式的累加器值。有时,第一个元素可以充当初始值。reduce()
和 reduceRight()
的行为类似于 fold()
和 foldRight()
,但分别使用第一个和最后一个元素作为初始值:
// FoldingLists/ReduceAndReduceRight.kt
import atomictest.eq
fun main() {
val chars = "A B C D E F G H I".split(" ")
chars.fold("X") { a, e -> "$a $e"} eq
"X A B C D E F G H I"
chars.foldRight("X") { a, e -> "$a $e" } eq
"A B C D E F G H I X"
chars.reduce { a, e -> "$a $e" } eq
"A B C D E F G H I"
chars.reduceRight { a, e -> "$a $e" } eq
"A B C D E F G H I"
}
runningFold()
和 runningReduce()
生成一个包含过程中所有中间步骤的 List
。List
中的最终值是 fold()
或 reduce()
的结果:
// FoldingLists/RunningFold.kt
import atomictest.eq
fun main() {
val list = listOf(11, 13, 17, 19)
list.fold(7) { sum, n ->
sum + n
} eq 67
list.runningFold(7) { sum, n ->
sum + n
} eq "[7, 18, 31, 48, 67]"
list.reduce { sum, n ->
sum + n
} eq 60
list.runningReduce { sum, n ->
sum + n
} eq "[11, 24, 41, 60]"
}
runningFold()
首先存储初始值(7
),然后存储每个中间结果。runningReduce()
会跟踪每个 sum
值。
练习和解答可以在 www.AtomicKotlin.com 找到。