开发者

Scala vals vs vars

开发者 https://www.devze.com 2023-01-26 23:46 出处:网络
I\'m pretty new to Scala but I like开发者_JS百科 to know what is the preferred way of solving this problem.Say I have a list of items and I want to know the total amount of the items that are checks.I

I'm pretty new to Scala but I like开发者_JS百科 to know what is the preferred way of solving this problem. Say I have a list of items and I want to know the total amount of the items that are checks. I could do something like so:

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

That would give me what I need, the sum of all checks in a immutable variable. But it does it with what seems like 3 iterations. Once to filter the checks, again to map the amounts and then the sum. Another way would be to do something like:

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

This gives me the same result but with 1 iteration and a mutable variable which seems fine too. But if I wanted to to extract more information, say the total number of checks, that would require more counters or mutable variables but I wouldn't have to iterate over the list again. Doesn't seem like the "functional" way of achieving what I need.

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

So if you find yourself needing a bunch of counters or totals on a list is it preferred to keep mutable variables or not worry about it do something along the lines of:

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

which seems easier to read and only uses vals


Another way of solving your problem in one iteration would be to use views or iterators:

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

or

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

This way the evaluation of the expression is delayed until the call of sum.

If your items are case classes you could also write it like this:

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


I find that speaking of doing "three iterations" is a bit misleading -- after all, each iteration does less work than a single iteration with everything. So it doesn't automatically follows that iterating three times will take longer than iterating once.

Creating temporary objects, now that is a concern, because you'll be hitting memory (even if cached), which isn't the case of the single iteration. In those cases, view will help, even though it adds more method calls to do the same work. Hopefully, JVM will optimize that away. See Moritz's answer for more information on views.


You may use foldLeft for that:

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

The following code will return a tuple (number of checks -> sum of amounts):

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

精彩评论

暂无评论...
验证码 换一张
取 消