Me encuentro utilizando una gran cantidad de mapas anidados, por ejemplo, un Mapa [Int, Mapa [Cadena, Establecer [Cadena]]], y me gustaría tener nuevos Mapas, Conjuntos, etc. creado automáticamente cuando accedo a una nueva clave. P.ej. algo así como lo siguiente:Diseñando un conveniente mapa valorado por defecto en Scala
val m = ...
m(1992)("foo") += "bar"
Tenga en cuenta que no quiero utilizar getOrElseUpdate aquí si no tengo que porque se pone bastante detallado cuando haya anidado mapas y oscurece lo que está sucediendo realmente en el código:
m.getOrElseUpdate(1992, Map[String, Set[String]]()).getOrElseUpdate("foo", Set[String]()) ++= "bar"
Así que estoy anulando el método "predeterminado" de HashMap. He intentado dos formas de hacer esto, pero ninguna es completamente satisfactoria. Mi primera solución era escribir un método que ha creado el mapa, pero parece que todavía tengo que especificar el tipo completo Mapa anidada cuando declaro la variable o las cosas no funcionan:
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)
^
Mi segunda solución era escribir una clase de fábrica con un método, y de esa manera solo tengo que declarar cada tipo una sola vez. Pero entonces cada vez que quiera un nuevo mapa valorada por defecto, tengo que ambas instancias de la clase de fábrica y luego llamar al método, que todavía parece un poco prolijo:
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)))
que realmente me gustaría tener algo tan simple como este:
val m = defaultingMap[Int](defaultingMap[String](Set[String]()))
¿Alguien ve la manera de hacer eso?
veo, la El truco es usar una capa de objeto acompañante encima de la clase de fábrica. Gracias, genial! Por curiosidad, ¿qué API usarías? Estoy tratando de evitar la atrocidad getOrElseUpdate anidada que mostré en el segundo bloque de código anterior. Estoy ciertamente abierto a otras formas de hacerlo. – Steve