【发布时间】:2013-02-20 01:43:17
【问题描述】:
我有这样的 Scala 代码
var i = 1
for(e <- array) {
acc += e * i
i += 1
}
我需要将数组中的第一个元素乘以 1,下一个元素乘以 2,下一个元素乘以 3,依此类推,将其全部添加到累加器中。我觉得在 Scala 中有更好的方法来做到这一点,甚至可以折叠?
【问题讨论】:
标签: arrays scala increment accumulator
我有这样的 Scala 代码
var i = 1
for(e <- array) {
acc += e * i
i += 1
}
我需要将数组中的第一个元素乘以 1,下一个元素乘以 2,下一个元素乘以 3,依此类推,将其全部添加到累加器中。我觉得在 Scala 中有更好的方法来做到这一点,甚至可以折叠?
【问题讨论】:
标签: arrays scala increment accumulator
val x = List(1,1,1,1,1,1)
(((0,1) /: x){case ((acc, mult), l) => (acc + (l * mult), mult + 1) })._1
换句话说,从 0 的累加器和 1 的乘数开始,将列表的每个元素折叠起来,将累加器更改为 acc + (l * mult) 并将乘数加 1。我们在最后得到最终的乘数同样,所以我们调用._1 来获取累加器。
编辑:正如@RexKerr 在下面的回答(和评论)中指出的那样,如果性能是一个主要问题,那么最好使用显式递归方法。
【讨论】:
我更喜欢阅读更简单的 zipWithIndex:
array.zipWithIndex.map { case (e, i) => e * (i + 1) }.sum
【讨论】:
我不确定我建议的方法是否更好,因为它更实用(== 执行速度会更慢):
(0 /: (array zipWithIndex)) {(a, i) => (i._1 * (i._2 + 1)) + a}
这确实对 http://www.scala-lang.org/api/current/index.html#scala.Array 中的 zipWithIndex 方法生成的数组执行 foldLeft
zipWithIndex 只是用它们的索引压缩集合的元素。
【讨论】:
“更好”取决于您的目标是什么。简短而清晰?可能
{ for (i <- array.indices; e = array(i)) yield (i+1)*e }.sum
或
array.indices.map(i => (i+1)*array(i)).sum
(或者稍微快一点,因为您可以随时创建中间体:
array.indices.iterator.map(i => (i+1)*array(i)).sum
)。
你通常应该简短而清晰。
快吗?然后你需要去老派:
var i = 0
var acc = 0
while (i < array.length) {
acc += (i+1)*array(i)
i += 1
}
或使用递归
def sum(a: Array[Int], i: Int = 0, acc: Int = 0): Int =
if (i >= a.length) acc else sum(a, i+1, (i+1)*a(i) + acc)
sum(array)
【讨论】:
Vector)来存储中间结果。使用iterator 可以防止这种情况——它会即时进行映射——所以尽管从一个项目推进到另一个项目会产生额外的开销,但总体而言它更快。但它没有那么短和清晰,所以只有在额外收集的开销有问题时才这样做。 (不过,我认为它比折叠更清晰。)