Kotlin中折叠和缩小之间的基本区别是什么?什么时候使用哪个?


130

我非常有此两种功能相混淆fold(),并reduce()在科特林,谁能给我以区别二者的具体例子吗?


2
折叠减少
佐伊

4
看看这个这个主题的深层原理的讨论
GhostCat

2
@LunarWatcher,我看到了这些文档,但没有得到,这是您发布的问题,您能举个例子吗?
TapanHP

1
@MattKlein完成
Jayson Minard,

Answers:


280

fold 接受一个初始值,传递给它的lambda的第一次调用将接收该初始值和该集合的第一个元素作为参数。

例如,采用以下代码来计算整数列表的总和:

listOf(1, 2, 3).fold(0) { sum, element -> sum + element }

对lambda的第一个调用将带有参数01

如果必须为操作提供某种默认值或参数,则可以传递初始值很有用。例如,如果您正在列表中寻找最大值,但由于某种原因想要返回至少10,则可以执行以下操作:

listOf(1, 6, 4).fold(10) { max, element ->
    if (element > max) element else max
}

reduce不需要初始值,而是从集合的第一个元素开始作为累加器(sum在以下示例中调用)。

例如,让我们再次对整数求和:

listOf(1, 2, 3).reduce { sum, element -> sum + element }

这里对lambda的第一个调用将带有参数12

reduce当您的操作除了要应用到的集合中的值不依赖于任何其他值时,可以使用。


47
很好的解释!我还要说,空集合不能减少,但可以折叠。
Miha_x64

看到,在Kotlin的初学者水平上,您给出的第一个示例可以通过一些步骤进行进一步解释,最后给出答案吗?会有所帮助
TapanHP

3
@TapanHP emptyList<Int>().reduce { acc, s -> acc + s }将产生异常,但是emptyList<Int>().fold(0) { acc, s -> acc + s }可以。
Miha_x64

31
reduce还强制lambda的返回值与列表成员的类型相同,而fold则不然。这是使列表的第一个元素即累加器的初始值的重要结果。
andresp

4
@andresp:出于完整性的注意:它不必是同一类型。列表成员也可以是累加器的子类型:确实可以工作listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }(列表类型为Int,而累加器类型声明为Number且实际上为Long)
Boris

11

我要指出的主要功能差异(在其他答案的注释中提到,但可能很难理解)是,如果对空集合执行此操作reduce 将引发异常

listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.

这是因为.reduce不知道在“无数据”的情况下要返回什么值。

将此与进行对比.fold,这需要您提供一个“起始值”,如果出现空集合,它将是默认值:

val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)

因此,即使您不想将集合汇总为其他类型(不相关)的单个元素(只会.fold让您这样做),如果起始集合可能为空,那么您也必须检查集合先设置大小,然后.reduce使用.fold

val collection: List<Int> = // collection of unknown size

val result1 = if (collection.isEmpty()) 0
              else collection.reduce { x, y -> x + y }

val result2 = collection.fold(0) { x, y -> x + y }

assertEquals(result1, result2)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.