使用默认值或可变映射的替代值填充可变映射以实现Scala函数记忆
我知道,使用默认值或可变映射的替代值填充可变映射以实现Scala函数记忆,scala,dictionary,default-value,Scala,Dictionary,Default Value,我知道,withDefault允许您指定一个函数,该函数将应用于一个键以生成默认映射值,但每次将未知键传递到映射时,该函数都会被调用,即使传递的是同一个键——这些值不会存储在映射中 scala> case class Foo(foo: String) {} defined class Foo scala> val m = mutable.OpenHashMap[String, Foo]().withDefault( s => { | println(s"crea
withDefault
允许您指定一个函数,该函数将应用于一个键以生成默认映射值,但每次将未知键传递到映射时,该函数都会被调用,即使传递的是同一个键——这些值不会存储在映射中
scala> case class Foo(foo: String) {}
defined class Foo
scala> val m = mutable.OpenHashMap[String, Foo]().withDefault( s => {
| println(s"creating new value for $s")
| Foo(s)
| })
m: scala.collection.mutable.Map[String,Foo] = Map()
scala> m("elvis")
creating value for elvis
res1: Foo = Foo(elvis)
scala> m("elvis")
creating value for elvis
res2: Foo = Foo(elvis)
很明显,这对于case类来说并不是什么大问题,但是如果值本身是可变集合,或者任何其他可变对象,这就没有多大帮助了。无论我在哪里访问地图,我都会使用getOrElseUpdate
。肯定有更优雅的解决方案吗
或者,因为在本例中,“default”函数实际上是我生成映射值的唯一方法,所以是否有一种优雅的、类似Scala的解决方案来缓存不涉及可变映射的函数返回值
ETA:为了清楚起见,我确实想为每个不同的键再次调用该函数。但是对于给定的键,我只想调用函数一次,下次再返回相同的对象。试试这个解决方案。它需要改进。但主要思想包括:
case class Foo(foo: String) {}
implicit class MapExt[K, V](m: mutable.Map[K, V]) {
def withCachedDefault(f: K => V): mutable.Map[K, V] = {
new mutable.Map[K, V] { // create wrapper around 'm'
override def apply(k: K): V = this.get(k) match {
case None =>
val v: V = f(k)
m.put(k, v) // caching new non-existing value
v
case Some(v) => v
}
override def get(key: K): Option[V] = m.get(key)
override def iterator: Iterator[(K, V)] = m.iterator
override def +=(kv: (K, V)): this.type = ??? /* TODO wrap m.+= */
override def -=(key: K): this.type = ??? /* TODO wrap m.-= */
}
}
}
val m = mutable.OpenHashMap[String, Foo]().withCachedDefault(s => {
println(s"creating new value for $s")
Foo(s)
})
println(m("elvis"))
println(m("elvis"))
println(m("word"))
println(m("word"))
// output:
//> creating new value for elvis
//> Foo(elvis)
//> Foo(elvis)
//> creating new value for word
//> Foo(word)
//> Foo(word)
该解决方案存在几个问题,需要改进。例如,可变状态的不可预测性:
val m = mutable.OpenHashMap[String, Foo]().withCachedDefault(fun1)
val m2 = m.withCachedDefault(fun2)
m2("elvis")
m2("new value")
等等。Scalas
mutable.Map
实际上被设计成子类,因此可以覆盖默认值。默认的default
只抛出一个NoSuchElementException
,现有的withDefault(Value)
方法只覆盖default
。在缓存值时也可以这样做
scala> import scala.collection.mutable
import scala.collection.mutable
scala> case class Foo(foo: String)
defined class Foo
scala> val m = new mutable.OpenHashMap[String, Foo] {
| override def default(key: String): Foo = {
| println(s"creating new value for $key")
| val foo = Foo(key)
| put(key, foo)
| foo
| }
| }
m: scala.collection.mutable.OpenHashMap[String,Foo] = OpenHashMap()
scala> m("elvis")
creating new value for elvis
res0: Foo = Foo(elvis)
scala> m("elvis")
res1: Foo = Foo(elvis)
scala> m("not elvis")
creating new value for not elvis
res2: Foo = Foo(not elvis)
看见另请参见。我知道getOrElseUpdate
,但是(正如我在问题中提到的)我不想在我访问地图的任何地方使用它。computeIfAbsent
在Scala中可用吗?它们基本上是一样的。实际上,我不记得Java 8支持的状态是在Scala中……难道你不能继承mutable.Map
并重写get
方法,用定义的函数调用getOrElseUpdate
吗?啊,我没有仔细查看computeIfAbsent
文档。不,getOrElseUpdate()
调用get()
(),因此覆盖get()
将不起作用。但是我可以重写get()
,或多或少地做getOrElseUpdate()
所做的事情。如果没有比这更优雅的了,我想这是我的退路。这可能是我能做的最好的了。如果没有更好的结果,我几天后会接受的。