Разработка удобного значения по умолчанию оцененная карта в Scala

Я использую много вложенных карт, например, Карты [Интервал, Карта [Строка, Набор [Строка]]], и я хотел бы иметь новые Карты, Наборы, и т.д. созданные автоматически, когда я получаю доступ к новому ключу. Например, что-то как следующее:

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

Обратите внимание, что я не хочу использовать getOrElseUpdate здесь, если я не имею к тому, потому что это становится довольно подробным, когда Вы вложили карты, и затеняет то, что на самом деле продолжается в коде:

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

Кто-либо видит способ сделать это?

8
задан Steve 6 July 2010 в 15:01
поделиться

2 ответа

В 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, поэтому необходим трюк с классом для захвата типа ключа.

Между прочим: я не думаю, что полученный API очень понятен. Мне особенно не нравится доступ к карте с побочными эффектами.

5
ответ дан 5 December 2019 в 20:13
поделиться

Оказывается, мне также нужно расширить MapLike, иначе, когда я вызываю filter, map и т. Д., Моя карта со значениями по умолчанию снова превратится в обычную Map без семантики по умолчанию. Вот вариант решения mkneissl, который правильно работает с фильтром, картой и т. Д.

import scala.collection.mutable.{MapLike,Map,HashMap}

class DefaultingMap[K, V](defaultValue: => V) extends HashMap[K, V]
with MapLike[K, V, DefaultingMap[K, V]] {
  override def empty = new DefaultingMap[K, V](defaultValue)
  override def default(key: K): V = {
    val result = this.defaultValue
    this(key) = result
    result
  }
}

object DefaultingMap {
  def apply[K] = new Factory[K]
  class Factory[K] {
    def apply[V](defaultValue: => V) = new DefaultingMap[K, V](defaultValue)
  }
}

И вот, в действии, правильное действие с фильтром:

scala> val m = DefaultingMap[String](0)
m: DefaultingMap[String,Int] = Map()

scala> for (s <- "the big black bug bit the big black bear".split(" ")) m(s) += 1

scala> val m2 = m.filter{case (_, count) => count > 1}
m2: DefaultingMap[String,Int] = Map((the,2), (big,2), (black,2))
3
ответ дан 5 December 2019 в 20:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: