Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala vals vs vars

Tags:

scala

I'm pretty new to Scala but I like 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

like image 369
Mark Spangler Avatar asked Nov 29 '22 17:11

Mark Spangler


2 Answers

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
like image 83
Moritz Avatar answered Dec 15 '22 04:12

Moritz


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.

like image 38
Daniel C. Sobral Avatar answered Dec 15 '22 04:12

Daniel C. Sobral