scala - 在 Scala 中设计一个方便的默认值映射

标签 scala collections type-inference

我发现自己使用了很多嵌套映射,例如 Map[Int, Map[String, Set[String]]],并且我希望在访问新 key 时自动创建新的 Maps、Sets 等。例如。类似于以下内容:

val m = ...
m(1992)("foo") += "bar"

请注意,如果不需要,我不想在这里使用 getOrElseUpdate ,因为当您嵌套 map 时它会变得非常冗长,并且会掩盖代码中实际发生的事情:
m.getOrElseUpdate(1992, Map[String, Set[String]]()).getOrElseUpdate("foo", Set[String]()) ++= "bar"

所以我重写了 HashMap 的“默认”方法。我尝试了两种方法来做到这一点,但都不是完全令人满意的。我的第一个解决方案是编写一个创建映射的方法,但是当我声明变量或事情不起作用时,我似乎仍然必须指定完整的嵌套映射类型:
scala> def defaultingMap[K, V](defaultValue: => V): Map[K, V] = new HashMap[K, V] {                      |   override def default(key: K) = {
 |     val result = defaultValue
 |     this(key) = result
 |     result
 |   }
 | }
defaultingMap: [K,V](defaultValue: => V)scala.collection.mutable.Map[K,V]

scala> val m: Map[Int, Map[String, Set[String]]] = defaultingMap(defaultingMap(Set[String]()))
m: scala.collection.mutable.Map[Int,scala.collection.mutable.Map[String,scala.collection.mutable.Set[String]]] = Map()

scala> m(1992)("foo") += "bar"; println(m)                                                    
Map(1992 -> Map(foo -> Set(bar)))

scala> val m = defaultingMap(defaultingMap(Set[String]()))
m: scala.collection.mutable.Map[Nothing,scala.collection.mutable.Map[Nothing,scala.collection.mutable.Set[String]]] = Map()

scala> m(1992)("foo") += "bar"; println(m)
<console>:11: error: type mismatch;
 found   : Int(1992)
 required: Nothing
       m(1992)("foo") += "bar"; println(m)
         ^

我的第二个解决方案是编写一个带有方法的工厂类,这样我只需声明一次每种类型。但是每次我想要一个新的默认值映射时,我都必须实例化工厂类,然后调用方法,这似乎还是有点冗长:
scala> class Factory[K] {                                       
 |   def create[V](defaultValue: => V) = new HashMap[K, V] {
 |     override def default(key: K) = {                     
 |       val result = defaultValue                          
 |       this(key) = result                                 
 |       result                                             
 |     }                                                    
 |   }                                                      
 | }                                                        
defined class Factory

scala> val m = new Factory[Int].create(new Factory[String].create(Set[String]()))
m: scala.collection.mutable.HashMap[Int,scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[String]]] = Map()

scala> m(1992)("foo") += "bar"; println(m)
Map(1992 -> Map(foo -> Set(bar)))

我真的很想拥有像这样简单的东西:
val m = defaultingMap[Int](defaultingMap[String](Set[String]()))

有人看到这样做的方法吗?

最佳答案

使用 Scala 2.8:

object DefaultingMap {
  import collection.mutable
  class defaultingMap[K] {
    def apply[V](v: V): mutable.Map[K,V] = new mutable.HashMap[K,V] {
      override def default(k: K): V = {
        this(k) = v
        v
      }
    }
  }
  object defaultingMap {
    def apply[K] = new defaultingMap[K]
  }

  def main(args: Array[String]) {
    val d4 = defaultingMap[Int](4)
    assert(d4(3) == 4)
    val m = defaultingMap[Int](defaultingMap[String](Set[String]()))
    m(1992)("foo") += "bar"
    println(m)
  }
}

您不能在 Scala 中使用 curry 类型参数,因此需要使用类来捕获键类型。

顺便说一句:我认为生成的 API 不是很清楚。我特别不喜欢有副作用的 map 访问。

关于scala - 在 Scala 中设计一个方便的默认值映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3187411/

相关文章:

scala - 如何在 Scala 中使用 websockets 创建多个聊天室?

Java 有界泛型 : Type inference bug?(方法调用,JLS 15.12.2.7)

Scala:回归有它的位置

java - removeFirstOccurrence 和删除之间的区别

java - java注解可以像HashMap这样复杂的返回类型吗

Java 8 对浮点值进行排序

scala - 在 Scala 2 中,使用 .toSet 生成的 Set 类型推断失败?

Scalaz ·克莱斯利问题

Scala MapType 和 Tuple 作为 KeyValue

scala - 返回类型为复杂对象数组的Scala UDAF