Kotlin中fold和reduce之间的基本区别是什么?什么时候用哪个?

Tap*_*nHP 96 reduce fold kotlin

我正在阅读Kotlin的基础知识,我对Kotlin中的函数fold()和reduce()非常困惑,有人能给我一个区分两者的具体例子吗?

zsm*_*b13 210

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

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

listOf(1, 2, 3).fold(0) { sum, element -> sum + element }
Run Code Online (Sandbox Code Playgroud)

第一次调用lambda将使用参数01.

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

listOf(1, 6, 4).fold(10) { max, element ->
    if (element > max) element else max
}
Run Code Online (Sandbox Code Playgroud)

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

例如,让我们再做一次整数:

listOf(1, 2, 3).reduce { sum, element -> sum + element }
Run Code Online (Sandbox Code Playgroud)

这里对lambda的第一次调用将使用参数12.

reduce当您的操作不依赖于您正在应用它的集合中的值以外的任何值时,您可以使用.

  • 很好的解释!我也会说,空集不能减少,但可以折叠. (35认同)
  • reduce也强​​制lambda的返回与列表成员的类型相同,而fold则不然.这是制作列表的第一个元素(累加器的初始值)的重要结果. (24认同)
  • @andresp:就像完整性的注释一样:它不一定是_same_类型.列表成员也可以是累加器的子类型:这可以工作`listOf <Int>(1,2).reduce {acc:Number,i:Int - > acc.toLong()+ i}`(列表 - type是Int,而accumulator类型声明为Number,实际上是Long) (3认同)
  • @TapanHP`emptyList &lt;Int&gt;()。reduce {acc,s-&gt; acc + s}`将产生一个异常,但是`emptyList &lt;Int&gt;()。fold(0){acc,s-&gt; acc + s}可以。 (2认同)

Dim*_*ski 18

其他答案都没有提到的另一个区别如下:

操作的结果reduce将始终与要减少的数据具有相同的类型(或超类型)。从方法的定义我们可以看出reduce

public inline fun <S, T : S> Iterable<T>.reduce(operation: (acc: S, T) -> S): S {
    val iterator = this.iterator()
    if (!iterator.hasNext()) throw UnsupportedOperationException("Empty collection can't be reduced.")
    var accumulator: S = iterator.next()
    while (iterator.hasNext()) {
        accumulator = operation(accumulator, iterator.next())
    }
    return accumulator
}
Run Code Online (Sandbox Code Playgroud)

另一方面,折叠操作的结果可以是任何结果,因为在设置初始值时没有限制。例如,假设我们有一个包含字母和数字的字符串。我们想要计算所有数字的总和。我们可以通过折叠轻松做到这一点:

val string = "1a2b3"
val result: Int = string.fold(0, { currentSum: Int, char: Char ->
    if (char.isDigit())
        currentSum + Character.getNumericValue(char)
    else currentSum
})

//result is equal to 6
Run Code Online (Sandbox Code Playgroud)

  • 这是最重要的。折叠非常适合对对象列表进行数学运算。 (2认同)

Tar*_*n A 7

简单的答案

减少和折叠的结果是“项目列表转换单个项目”。

在折叠的情况下,除了列表之外,我们还提供 1 个额外参数,但在减少的情况下,仅考虑列表中的项目。

折叠

listOf("AC","Fridge").fold("stabilizer") { freeGift, itemBought -> freeGift + itemBought }

//output: stabilizerACFridge
Run Code Online (Sandbox Code Playgroud)

在上述情况下,想想从商店购买的空调、冰箱,他们赠送稳定器作为礼物(这将是折叠中传递的参数)。因此,您可以同时获得所有 3 件物品。请注意,免费礼物只能使用一次,即第一次迭代。

减少

在reduce的情况下,我们获取列表中的项目作为参数,并可以对其执行所需的转换。

listOf("AC","Fridge").reduce { itemBought1, itemBought2 -> itemBought1 + itemBought2 }

//output: ACFridge
Run Code Online (Sandbox Code Playgroud)


Mat*_*ein 5

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

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

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

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

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

因此,即使您不想将集合汇总为不同(不相关)类型的单个元素(只会.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)
Run Code Online (Sandbox Code Playgroud)


Gk *_*mon 5

reduce - 该reduce()方法将给定的集合转换为单个结果

val numbers: List<Int> = listOf(1, 2, 3)
val sum: Int = numbers.reduce { acc, next -> acc + next }
//sum is 6 now.
Run Code Online (Sandbox Code Playgroud)

fold - 在前一个空列表的情况下会发生什么?实际上,没有正确的返回值,所以reduce()抛出一个RuntimeException

在这种情况下,fold是一个方便的工具。您可以通过它放置一个初始值 -

val sum: Int = numbers.fold(0, { acc, next -> acc + next })
Run Code Online (Sandbox Code Playgroud)

在这里,我们提供了初始值。相反, to reduce(),如果集合为,则将返回初始值,这将阻止您从 RuntimeException.