2010-11-24 55 views
4

我對Scala很新,但我想知道解決這個問題的首選方式是什麼。假設我有一個項目清單,我想知道檢查項目的總量。我可以這樣做:Scala vals vs vars

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

+1

`for`表達式實際上被編譯爲`map`,`filter`和`foreach`,所以它等價於你的第一個例子。 – Theo 2010-11-24 20:12:11

+1

這不再準確:for理解使用withFilter,而不是過濾器,除非集合沒有withFilter方法。 – extempore 2010-11-24 21:28:23

回答

6

您可以使用該foldLeft

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

下面的代碼將返回(支票號碼 - >量的總和)的元組:

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

忘記摺疊/這會給我所有的數據回到一個很好的元組 – 2010-11-24 19:33:43

8

在一次迭代中解決您的問題的另一種方法是使用視圖或迭代器:

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

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

這種方式表達的評價被延遲,直到的sum呼叫。

如果你的項目是case類,你也可以寫這樣的:

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

我發現,做「三個迭代」的說話是有點誤導 - 畢竟,每次迭代確實小於工作一切的一次迭代。所以它不會自動遵循迭代三次需要比迭代一次更長的時間。

創建臨時對象,現在是一個問題,因爲你會碰到內存(即使緩存),這不是單次迭代的情況。在這些情況下,view將有所幫助,即使它增加了更多的方法調用來完成相同的工作。希望JVM能夠優化這一點。有關視圖的更多信息,請參閱Moritzanswer