gpt4 book ai didi

scala - 缩小、折叠或扫描(左/右)?

转载 作者:行者123 更新时间:2023-12-03 04:14:56 25 4
gpt4 key购买 nike

什么时候应该使用 reduceLeftreduceRightfoldLeftfoldRightscanLeft > 或 scanRight

我想要对它们的差异有一个直观/概述 - 可能有一些简单的例子。

最佳答案

一般来说,所有 6 个折叠函数都会对集合的每个元素应用二元运算符。每个步骤的结果都会传递到下一步(作为二元运算符的两个参数之一的输入)。这样我们就可以累积一个结果。

reduceLeftreduceRight 累积单个结果。

foldLeftfoldRight 使用起始值累积单个结果。

scanLeftscanRight 使用起始值累积中间累积结果的集合。

积累

从左往前...

通过元素集合 abc 和二元运算符 add,我们可以探索从集合的 LEFT 元素(从 A至 C):

val abc = List("A", "B", "C")

def add(res: String, x: String) = {
println(s"op: $res + $x = ${res + x}")
res + x
}

abc.reduceLeft(add)
// op: A + B = AB
// op: AB + C = ABC // accumulates value AB in *first* operator arg `res`
// res: String = ABC

abc.foldLeft("z")(add) // with start value "z"
// op: z + A = zA // initial extra operation
// op: zA + B = zAB
// op: zAB + C = zABC
// res: String = zABC

abc.scanLeft("z")(add)
// op: z + A = zA // same operations as foldLeft above...
// op: zA + B = zAB
// op: zAB + C = zABC
// res: List[String] = List(z, zA, zAB, zABC) // maps intermediate results


从右往后...

如果我们从右边的元素开始向后(从 C 到 A),我们会注意到现在二元运算符的第二个参数会累加结果(运算符是相同的,我们只是交换了参数名称以明确它们的角色):

def add(x: String, res: String) = {
println(s"op: $x + $res = ${x + res}")
x + res
}

abc.reduceRight(add)
// op: B + C = BC
// op: A + BC = ABC // accumulates value BC in *second* operator arg `res`
// res: String = ABC

abc.foldRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: String = ABCz

abc.scanRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: List[String] = List(ABCz, BCz, Cz, z)

.

去累积

从左往前...

如果我们要通过从集合的左侧元素开始减法来去累积一些结果,我们将通过我们的第一个参数res来累积结果二元运算符:

val xs = List(1, 2, 3, 4)

def minus(res: Int, x: Int) = {
println(s"op: $res - $x = ${res - x}")
res - x
}

xs.reduceLeft(minus)
// op: 1 - 2 = -1
// op: -1 - 3 = -4 // de-cumulates value -1 in *first* operator arg `res`
// op: -4 - 4 = -8
// res: Int = -8

xs.foldLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: Int = -10

xs.scanLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: List[Int] = List(0, -1, -3, -6, -10)


从右往后...

但是现在请注意 xRight 的变体!请记住,xRight 变量中的(去)累积值被传递给二元运算符 minus第二参数res:

def minus(x: Int, res: Int) = {
println(s"op: $x - $res = ${x - res}")
x - res
}

xs.reduceRight(minus)
// op: 3 - 4 = -1
// op: 2 - -1 = 3 // de-cumulates value -1 in *second* operator arg `res`
// op: 1 - 3 = -2
// res: Int = -2

xs.foldRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: Int = -2

xs.scanRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: List[Int] = List(-2, 3, -1, 4, 0)

最后一个 List(-2, 3, -1, 4, 0) 可能不是您直观所期望的!

如您所见,您只需运行 scanX 即可检查 FoldX 正在执行的操作,并调试每个步骤的累积结果。

底线

  • 使用 reduceLeftreduceRight 累积结果。
  • 如果有起始值,请使用 foldLeftfoldRight 累积结果。
  • 使用 scanLeftscanRight 累积中间结果集合。

  • 如果您想向前浏览集合,请使用 xLeft 变体。

  • 如果您想向后浏览集合,请使用 xRight 变体。

关于scala - 缩小、折叠或扫描(左/右)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17408880/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com