Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/google-app-engine/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用默认值或可变映射的替代值填充可变映射以实现Scala函数记忆_Scala_Dictionary_Default Value - Fatal编程技术网

使用默认值或可变映射的替代值填充可变映射以实现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()
所做的事情。如果没有比这更优雅的了,我想这是我的退路。这可能是我能做的最好的了。如果没有更好的结果,我几天后会接受的。