【问题标题】:How does foo(a)(b) += x work in Scala?foo(a)(b) += x 在 Scala 中是如何工作的?
【发布时间】:2013-10-08 23:50:38
【问题描述】:

我一直在用可变地图计算出现次数:

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

现在bar(a) += b 工作正常,无论bar 中是否已经存在密钥a(在这种情况下,它将被添加)。

我用可变映射的可变映射尝试了同样的事情:

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,如果它以前不存在)?

编辑:作为Perseids pointed outfoo(a)(b) += x 将改变可变的默认值。这是一个理想的功能吗? 按照DaoWen 的建议使用getOrElseUpdate 似乎是克服这两个问题的最佳方法。但是,虽然这对于 Int => Int => Int 类型的函数很有效,但对于 Int => Int => Int => Int => Int 类型的函数来说确实很麻烦。所以我仍然很高兴有任何建议!

【问题讨论】:

  • foo(a)(在第一个示例中)和foo(a)(b)(在第二个示例中)都是 Int 类型。 Int class 中没有 += 方法。
  • 完全正确,我处于 ​​scala-daze 效果下!
  • "foo(a)(b) += x 将改变可变的默认值。这是一个理想的功能吗?" 我认为这是在设计时考虑到不可变对象.可以返回与默认值相同的不可变对象(例如,Int 或不可变的Map)。它不适用于可变值...
  • 真的有必要在地图中使用这么多层次的嵌套吗?您可以只使用元组作为键吗?换句话说,您将拥有(Int, Int, Int, Int) => Int,而不是Int => Int => Int => Int => Int

标签: scala scala-collections


【解决方案1】:

这实际上是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 在下面的 cmets 中指出的那样,withDefaultValue 采用 by-value 参数。这意味着每次您从地图中获取未设置的键时,它都会返回相同的可变地图实例!这是您应该考虑使用 getOrElseUpdate 的另一个原因(它使用 by-name 参数),或者至少是withDefault,它接受一个函数。 (这一切都假设您实际上希望地图中的每个插槽都有不同的地图实例......)

【讨论】:

  • 这个时态图是如何生成的?外部withDefaultValue 仅获得对 mutable.Map 的具体实例的引用,而不是工厂的引用。
  • 我刚刚玩了一点 REPL,你的解释与我的结果不符。我创建了一个数组 val a = Array[Int] (10) 并将其放入 withDefaultValue val bar = collection.mutable.Map[Int,Array[Int]]().withDefaultValue(a)。现在,bar(0) eq a 的计算结果为 true,即每次未找到密钥且 不是a 的副本时,bar 都会返回对 a 的相同原始引用。
  • @Perseids - 你是对的。我以为withDefaultValue 有一个 by-name 参数。它不会每次都返回新地图。我认为这是一个完全不同的问题......不过我会更新我的答案。
【解决方案2】:

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)的重复评估(我认为。)

【讨论】:

    猜你喜欢
    • 2012-08-27
    • 2011-03-26
    • 2015-02-09
    • 1970-01-01
    • 2015-09-08
    • 2019-01-27
    • 2016-10-26
    • 2015-12-30
    • 1970-01-01
    相关资源
    最近更新 更多