Ste*_*eve 8 collections scala type-inference
我发现自己使用了很多嵌套映射,例如Map [Int,Map [String,Set [String]]],我希望在访问新密钥时自动创建新的地图,集等.例如以下内容:
val m = ...
m(1992)("foo") += "bar"
Run Code Online (Sandbox Code Playgroud)
请注意,如果我不需要,我不想在这里使用getOrElseUpdate,因为当你有嵌套的地图并且隐藏代码中实际发生的事情时,它会变得非常冗长:
m.getOrElseUpdate(1992, Map[String, Set[String]]()).getOrElseUpdate("foo", Set[String]()) ++= "bar"
Run Code Online (Sandbox Code Playgroud)
所以我重写了HashMap的"默认"方法.我尝试了两种方法,但都没有完全令人满意.我的第一个解决方案是编写一个创建地图的方法,但是当我声明变量或事情不起作用时,似乎我仍然必须指定完整的嵌套Map类型:
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)
^
Run Code Online (Sandbox Code Playgroud)
我的第二个解决方案是用一个方法编写一个工厂类,这样我只需要一次声明每个类型.但是每次我想要一个新的默认值映射时,我必须实例化工厂类,然后调用方法,这看起来仍然有点冗长:
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)))
Run Code Online (Sandbox Code Playgroud)
我真的很喜欢这样简单:
val m = defaultingMap[Int](defaultingMap[String](Set[String]()))
Run Code Online (Sandbox Code Playgroud)
有人看到了这样做的方法吗?
使用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)
}
}
Run Code Online (Sandbox Code Playgroud)
您无法在Scala中调整类型参数,因此必须使用类来捕获键类型.
顺便说一句:我不认为生成的API非常清楚.我特别不喜欢副作用地图访问.