Scala vals vs vars

时间:2010-11-24 17:08:24

标签: scala

我对Scala很新,但我想知道解决这个问题的首选方法是什么。假设我有一个项目列表,我想知道检查项目的总数。我可以这样做:

val total = items.filter(_.itemType == CHECK).map(._amount).sum

这会给我我需要的东西,即不可变变量中所有检查的总和。但它看起来像是3次迭代。一旦过滤检查,再次映射金额,然后总和。另一种方法是做一些事情:

var total = new BigDecimal(0)
for (
    item <- items
    if item.itemType == CHECK
) total += item.amount

这给了我相同的结果但是有1次迭代和一个可变变量,看起来也很好。但是,如果我想提取更多信息,比如支票总数,那就需要更多的计数器或可变变量,但我不必再次遍历列表。似乎不是实现我需要的“功能”方式。

var numOfChecks = 0
var total = new BigDecimal(0)
items.foreach { item =>
    if (item.itemType == CHECK) {
        numOfChecks += 1
        total += item.amount
    }
}

因此,如果您发现自己需要列表中的一堆计数器或总计,则首选保留可变变量或不担心它会执行以下操作:

val checks = items.filter(_.itemType == CHECK)
val total = checks.map(_.amount).sum
return (checks.size, total)

似乎更容易阅读,只使用vals

3 个答案:

答案 0 :(得分:8)

在一次迭代中解决问题的另一种方法是使用视图或迭代器:

items.iterator.filter(_.itemType == CHECK).map(._amount).sum

items.view.filter(_.itemType == CHECK).map(._amount).sum

这样,表达式的评估会延迟到调用sum

如果您的项目是案例类,您也可以这样写:

items.iterator collect { case Item(amount, CHECK) => amount } sum

答案 1 :(得分:7)

我发现说“三次迭代”有点误导 - 毕竟,每次迭代都比一次迭代完成的工作少。所以它不会自动跟随迭代三次将比迭代一次更长。

创建临时对象,现在 是一个问题,因为你将会遇到内存(即使是缓存),这不是单次迭代的情况。在这些情况下,view会有所帮助,即使它添加了更多方法调用来执行相同的工作。希望JVM能够优化它。有关观看次数的详情,请参见Moritzanswer

答案 2 :(得分:6)

您可以使用foldLeft

(0 /: items) ((total, item) => 
   if(item.itemType == CHECK) 
      total + item.amount 
   else 
      total
)

以下代码将返回元组(支票数量 - &gt;金额总和):

((0, 0) /: items) ((total, item) => 
   if(item.itemType == CHECK) 
      (total._1 + 1, total._2 + item.amount) 
   else 
      total
)