Kotlin 中 fold 和 reduce 的区别,什么时候使用哪个?
Difference between fold and reduce in Kotlin, When to use which?
我对 Kotlin 中的 fold()
和 reduce()
这两个函数感到很困惑,谁能给我一个区分这两个函数的具体例子?
fold
接受一个初始值,您传递给它的 lambda 的第一次调用将接收该初始值和集合的第一个元素作为参数。
例如,采用以下计算整数列表总和的代码:
listOf(1, 2, 3).fold(0) { sum, element -> sum + element }
对 lambda 的第一次调用将使用参数 0
和 1
。
如果您必须为操作提供某种默认值或参数,则能够传入初始值非常有用。例如,如果您正在寻找列表中的最大值,但由于某种原因想要 return 至少 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 的第一次调用将使用参数 1
和 2
。
当您的操作不依赖于您应用它的集合中的值以外的任何值时,您可以使用 reduce
。
我要指出的主要功能差异(在另一个答案的评论中提到,但可能难以理解)是 reduce
会抛出异常 如果在空 collection.
上执行
listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.
这是因为.reduce
不知道在"no data"的情况下return的值是多少。
将此与 .fold
进行对比,后者要求您提供一个 "starting value",如果为空 collection:
,这将是默认值
val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)
因此,即使您不想将 collection 聚合为不同 (non-related) 类型的单个元素(只有 .fold
可以让您这样做),如果您的起始 collection 可能为空,那么您必须先检查您的 collection 大小,然后再检查 .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)
reduce - reduce()
方法将给定的 collection 转换为 single result.
val numbers: List<Int> = listOf(1, 2, 3)
val sum: Int = numbers.reduce { acc, next -> acc + next }
//sum is 6 now.
fold - 在之前的空列表情况下会发生什么?实际上,return 没有正确的值,所以 reduce()
抛出一个 RuntimeException
在这种情况下,fold
是一个方便的工具。你可以给它一个初始值 -
val sum: Int = numbers.fold(0, { acc, next -> acc + next })
在这里,我们提供了初始值。相反,对于 reduce()
,如果集合是 empty,则初始值将被 returned,这将阻止您 RuntimeException
.
none 提到的其他答案的另一个区别如下:
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
}
另一方面,折叠操作的结果可以是任何东西,因为在设置初始值时没有限制。
因此,例如,假设我们有一个包含字母和数字的字符串。我们要计算所有数字的总和。
我们可以使用 fold:
轻松做到这一点
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
简单回答
reduce 和 fold 的结果都是“项目列表 将被转换进入单项"。
在 fold 的情况下,我们提供了除列表之外的 1 个额外参数,但在 reduce 的情况下,只有列表中的项目将被考虑过。
折叠
listOf("AC","Fridge").fold("stabilizer") { freeGift, itemBought -> freeGift + itemBought }
//output: stabilizerACFridge
在上面的例子中,想想空调,从商店买的冰箱,他们把稳定器作为礼物赠送(这将是折叠中传递的参数)。所以,你得到所有 3 件物品 together.Please 注意 freeGift将仅可用一次,即第一次迭代。
减少
在 reduce 的情况下,我们将列表中的项目作为参数,并可以对其执行所需的转换。
listOf("AC","Fridge").reduce { itemBought1, itemBought2 -> itemBought1 + itemBought2 }
//output: ACFridge
我对 Kotlin 中的 fold()
和 reduce()
这两个函数感到很困惑,谁能给我一个区分这两个函数的具体例子?
fold
接受一个初始值,您传递给它的 lambda 的第一次调用将接收该初始值和集合的第一个元素作为参数。
例如,采用以下计算整数列表总和的代码:
listOf(1, 2, 3).fold(0) { sum, element -> sum + element }
对 lambda 的第一次调用将使用参数 0
和 1
。
如果您必须为操作提供某种默认值或参数,则能够传入初始值非常有用。例如,如果您正在寻找列表中的最大值,但由于某种原因想要 return 至少 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 的第一次调用将使用参数 1
和 2
。
当您的操作不依赖于您应用它的集合中的值以外的任何值时,您可以使用 reduce
。
我要指出的主要功能差异(在另一个答案的评论中提到,但可能难以理解)是 reduce
会抛出异常 如果在空 collection.
listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.
这是因为.reduce
不知道在"no data"的情况下return的值是多少。
将此与 .fold
进行对比,后者要求您提供一个 "starting value",如果为空 collection:
val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)
因此,即使您不想将 collection 聚合为不同 (non-related) 类型的单个元素(只有 .fold
可以让您这样做),如果您的起始 collection 可能为空,那么您必须先检查您的 collection 大小,然后再检查 .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)
reduce - reduce()
方法将给定的 collection 转换为 single result.
val numbers: List<Int> = listOf(1, 2, 3)
val sum: Int = numbers.reduce { acc, next -> acc + next }
//sum is 6 now.
fold - 在之前的空列表情况下会发生什么?实际上,return 没有正确的值,所以 reduce()
抛出一个 RuntimeException
在这种情况下,fold
是一个方便的工具。你可以给它一个初始值 -
val sum: Int = numbers.fold(0, { acc, next -> acc + next })
在这里,我们提供了初始值。相反,对于 reduce()
,如果集合是 empty,则初始值将被 returned,这将阻止您 RuntimeException
.
none 提到的其他答案的另一个区别如下:
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
}
另一方面,折叠操作的结果可以是任何东西,因为在设置初始值时没有限制。 因此,例如,假设我们有一个包含字母和数字的字符串。我们要计算所有数字的总和。 我们可以使用 fold:
轻松做到这一点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
简单回答
reduce 和 fold 的结果都是“项目列表 将被转换进入单项"。
在 fold 的情况下,我们提供了除列表之外的 1 个额外参数,但在 reduce 的情况下,只有列表中的项目将被考虑过。
折叠
listOf("AC","Fridge").fold("stabilizer") { freeGift, itemBought -> freeGift + itemBought }
//output: stabilizerACFridge
在上面的例子中,想想空调,从商店买的冰箱,他们把稳定器作为礼物赠送(这将是折叠中传递的参数)。所以,你得到所有 3 件物品 together.Please 注意 freeGift将仅可用一次,即第一次迭代。
减少
在 reduce 的情况下,我们将列表中的项目作为参数,并可以对其执行所需的转换。
listOf("AC","Fridge").reduce { itemBought1, itemBought2 -> itemBought1 + itemBought2 }
//output: ACFridge