Scala:生成折叠的中间结果

wnvonmuf  于 2023-01-02  发布在  Scala
关注(0)|答案(4)|浏览(131)

我曾经多次遇到过在整个Map操作中维护状态的问题。
给定一个List[Int],将每个元素Map到所有前面的元素和它自己的总和。
所以1,2,3变成了1,1 + 2,1 +2+3.
我想出的一个解决办法是:

scala> val a = 1 to 5                                                
a: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(1, 2, 3, 4, 5)

scala> a.foldLeft(List(0)){ case (l,i) => (l.head + i) :: l }.reverse
res3: List[Int] = List(0, 1, 3, 6, 10, 15)

但不知何故,我觉得必须有一个更简单的解决办法。

e4eetjau

e4eetjau1#

你要计算的是部分和的序列。
计算这种累加的一般运算不是fold,而是scan,尽管scan可以用fold来表示(fold实际上是scan生成的列表的最后一个元素)。

scala> List(1,2,3).scanLeft(0)(_ + _)
res26: List[Int] = List(0, 1, 3, 6)
jslywgbw

jslywgbw2#

@Dario给出了答案,但只是补充一下scala库提供了scanLeft:

scala> List(1,2,3).scanLeft(0)(_ + _)
res26: List[Int] = List(0, 1, 3, 6)
xggvc2p6

xggvc2p63#

scan的答案是最好的答案,但值得注意的是,你可以使折叠看起来更漂亮和/或比你的问题更短。首先,你不需要使用模式匹配:

a.foldLeft(List(0)){ (l,i) => (l.head + i) :: l }.reverse

其次,请注意foldLeft有一个缩写:

(List(0) /: a){ (l,i) => (l.head + i) :: l }.reverse

第三,请注意,如果需要,可以使用一个可以高效追加的集合,这样就不需要反转:

(Vector(0) /: a){ (v,i) => v :+ (v.last + i) }

因此,虽然它不如scanLeft紧凑:

a.scanLeft(0)(_ + _)

还不算太糟。

mrzz3bfm

mrzz3bfm4#

我和其他人一样喜欢折叠,但一个不太FP的答案是非常简洁和可读的:

a.map{var v=0; x=>{v+=x; v}}

相关问题