在Scala中设计一个方便的默认值映射 [英] Designing a convenient default valued map in Scala

查看:91
本文介绍了在Scala中设计一个方便的默认值映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现自己使用了大量的嵌套映射,例如Map [Int,Map [String,Set [String]]],而且我想在访问时自动创建新的地图,集合等新密钥。例如。如下所示:

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

注意,如果我不需要,我不想在这里使用getOrElseUpdate当你有嵌套的地图,并掩盖了代码中实际发生的事情时,它变得很冗长。

  m.getOrElseUpdate [String,Set [String]]())。getOrElseUpdate(foo,Set [String]())++ =bar

所以我重写了HashMap的默认方法。我试过两种方法这样做,但两者都不完全令人满意。我的第一个解决方案是写一个创建地图的方法,但似乎我仍然必须指定完整的嵌套Map类型当我声明的变量或事情不工作:

  scala> def defaultingMap [K,V](defaultValue:=> V):Map [K,V] = new HashMap [K,V] override def default(key:K)= {
| Val result = defaultValue
|这(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;
发现: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](set [String] ))
m(1992)(foo)+ =bar
println(m)
}
}
pre>

你不能在Scala中curry类型参数,因此捕获键类型的类的技巧是必要的。



顺便说一下:我不认为生成的API非常清楚。我特别不喜欢副作用的地图访问。


I find myself using a lot of nested maps, e.g a Map[Int, Map[String, Set[String]]], and I'd like to have new Maps, Sets, etc. created automatically when I access a new key. E.g. something like the following:

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

Note that I don't want to use getOrElseUpdate here if I don't have to because it gets pretty verbose when you have nested maps and obscures what's actually going on in the code:

m.getOrElseUpdate(1992, Map[String, Set[String]]()).getOrElseUpdate("foo", Set[String]()) ++= "bar"

So I'm overriding HashMap's "default" method. I've tried two ways of doing this, but neither is fully satisfactory. My first solution was to write a method that created the map, but it seems that I still have to specify the full nested Map type when I declare the variable or things don't work:

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)
         ^

My second solution was to write a factory class with a method, and that way I only have to declare each type a single time. But then each time I want a new default valued map, I have to both instantiate the factory class and then call the method, which still seems a little verbose:

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)))

I'd really like to have something as simple as this:

val m = defaultingMap[Int](defaultingMap[String](Set[String]()))

Anyone see a way to do that?

解决方案

With 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)
  }
}

You can't curry type parameters in Scala, therefore the trick with the class to capture the key type is necessary.

By the way: I don't think that the resulting API is very clear. I particularly dislike the side-effecting map access.

这篇关于在Scala中设计一个方便的默认值映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆