foo(a)(b)+ = x如何在Scala中工作?

时间:2013-10-08 23:50:39

标签: scala scala-collections

我一直用可变地图来计算出现的情况:

var bar = collection.mutable.Map[Int, Int]().withDefaultValue(0)

现在bar(a) += b工作得很好,无论a中是否存在密钥bar已经存在(在这种情况下会被添加)。

我用可变地图的可变地图尝试了同样的事情:

var foo = collection.mutable.Map[Int, collection.mutable.Map[Int, Int]]().
          withDefaultValue(collection.mutable.Map().withDefaultValue(0))

如果没有语法糖,foo(a)(b) += x怎么看?

使用What are all the instances of syntactic sugar in Scala?我会假设它扩展为:

foo.apply(a).put(b, foo.apply(a).apply(b) + x)

但为什么这不会像介绍示例那样相应地更新foo(例如foo如果之前不存在,则a将不具有密钥foo(a)(b) += x的专用值?

修改Perseids pointed outgetOrElseUpdate将更改可变的默认值。这是一个理想的功能吗? 使用DaoWen建议的Int => Int => Int似乎是克服这两个问题的最佳方法。但是虽然这对于Int => Int => Int => Int => Int类型的函数很有效,但对于{{1}}类型的函数来说它变得非常麻烦。所以我仍然对任何建议感到高兴!

2 个答案:

答案 0 :(得分:3)

这实际上是withDefaultValue的问题,而不是+=运算符。如果给定的密钥不存在,则withDefaultValue的第一个foo会返回一个新的可变映射。当您执行查找foo(a)(b)时,如果foo(a)不存在,则会返回一个新地图,我们将其称为tmpfoo(a)(b) += x然后基本上扩展到:

val tmp = foo(a)
tmp(b) += x

问题是tmp只更新了+=,而不是foo。因此,您的更新发生在tmp,但tmp在通话后被丢弃,因为它永远不会存储在任何地方。

如果您希望更新父地图,可能需要使用getOrElseUpdate而不是依赖withDefaultValue


注意: 正如Perseids在下面的评论中指出的那样,withDefaultValue采用按值参数。这意味着每次从地图中获取未设置的密钥时,它都会返回相同的可变地图实例!这是您应该考虑使用getOrElseUpdate(使用 by-name 参数),或至少withDefault,它接受​​一个函数。 (这都假设您实际上想要地图中每个插槽的不同地图实例...)

答案 1 :(得分:1)

  

如果没有语法糖,foo(a)(b) += x怎么看?

这取决于foo(a)(b)返回的对象是否具有名为+=的方法。如果确实如此,则相当于:

foo.apply(a).apply(b).+=(x)

如果没有,则相当于:

foo.apply(a).update(b, foo.apply(a).apply(b).+(x))

除非没有foo.apply(a)的重复评估(我认为。)