Scala vals 与 vars

发布于 2024-10-04 09:46:12 字数 925 浏览 3 评论 0原文

我对 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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

栖迟 2024-10-11 09:46:12

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

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

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
红衣飘飘貌似仙 2024-10-11 09:46:12

我发现说进行“三次迭代”有点误导——毕竟,每次迭代所做的工作都比一次迭代完成的工作要少。因此,并不自动得出迭代 3 次会比迭代一次花费更长的时间。

创建临时对象,现在是一个问题,因为您将占用内存(即使已缓存),这不是单次迭代的情况。在这些情况下,view 会有所帮助,即使它添加了更多方法调用来完成相同的工作。希望 JVM 能够优化这一点。请参阅 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.

久随 2024-10-11 09:46:12

您可以使用 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
)

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
)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文