【问题标题】:Update a mutable map with default value in Scala在 Scala 中使用默认值更新可变映射
【发布时间】:2013-01-30 13:15:19
【问题描述】:

考虑以下代码,它计算列表中每个字符串的频率并将结果存储在可变映射中。这很好用,但我不明白 += 方法是在哪里定义的?!这是一些奇怪的隐式转换还是什么?我在某处看到了这段代码,但它没有包含对 += 的解释。

val list = List("a", "b", "a")
val counts = new scala.collection.mutable.HashMap[String, Int]().withDefaultValue(0)
list.foreach(counts(_) += 1)
counts
//> res7: scala.collection.mutable.Map[String,Int] = Map(a -> 2, b -> 1)

map 的 apply 返回一个 Int,但 Int 没有 +=,并且此方法使用新值更新 map,因此看起来 apply 返回一个具有 += 方法的可变整数。 .

【问题讨论】:

    标签: scala map


    【解决方案1】:

    这不是隐式转换 - 它是脱糖。 写作:

    x += 1
    

    去糖:

    x = x + 1
    

    如果x 的类上没有定义+= 方法。

    同理:

    counts("a") += 1
    

    去糖:

    counts("a") = counts("a") + 1
    

    因为counts("a")Int,而Int 没有定义+= 方法。

    另一方面,写作:

    x(expression1) = expression2
    

    取消对 Scala 中 update 方法的调用:

    x.update(expression1, expression2)
    

    每个可变的Map 都有一个update 方法defined - 它允许在映射中设置键。

    所以整个表达式被取消为:

    list.foreach(x => counts.update(x, counts(x) + 1))
    

    不要将此+= 与Scala 中mutable.Maps 上的+= 方法混淆。如果该键已经存在,该方法会更新映射中的条目,或者添加一个新的键值对。它返回this 引用,即相同的映射,因此您可以链接+= 调用。请参阅ScalaDocsource code

    【讨论】:

    • 是的,来自mutable.Map+= 永远不会在上面调用。 counts("a") += 1 被脱糖成 counts("a") = counts("a") + 1,在地图上进一步脱糖成 update 调用。
    • 好的,谢谢你们,双重脱糖解释是有道理的!我删除了基于@axel22 第一个答案的第一条评论,然后他修改了该评论。无论如何,现在它是有道理的,谢谢! :D
    【解决方案2】:

    当您想知道代码的一部分中发生了什么编译器魔法时,scalac -print 是您最好的朋友(请参阅this question)。

    如果您在 C.scala 所在的位置执行 scalac -print C.scala

    package test
    
    class C {
        def myMethod() {
            val counts = new scala.collection.mutable.HashMap[String, Int]().withDefaultValue(0)
            counts("a") += 1
        }
    }
    

    你得到

    package test {
      class C extends Object {
        def myMethod(): Unit = {
          val counts: collection.mutable.Map = new collection.mutable.HashMap().withDefaultValue(scala.Int.box(0));
          counts.update("a", scala.Int.box(scala.Int.unbox(counts.apply("a")).+(1)))
        };
        def <init>(): test.C = {
          C.super.<init>();
          ()
        }
      }
    

    这也让我感到意外,但显然 scalac 会转变

    map(key) =<op> rhs
    

    map.update(key, map.apply(key) <op> rhs)
    

    【讨论】:

      猜你喜欢
      • 2016-05-14
      • 1970-01-01
      • 1970-01-01
      • 2018-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-18
      相关资源
      最近更新 更多