折叠列表
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 找到。