更新不可变映射作为getOrElse的副作用

时间:2015-07-27 12:39:35

标签: scala

有时我使用Map作为记忆缓存。使用可变地图,我使用getOrElseUpdate

mutableMap.getOrElseUpdate(key, {
    val value = <compute the value>
    value
})

不可变地图没有getOrElseUpdate。所以我想这样做

immutableMap.getOrElse(key, {
    val value = <compute the value>
    immutableMap += key -> value
    value
})

这似乎在实践中起作用,我有充分的理由认为它在理论上有效,而且它的可读性或多或少 - 出于某些原因,这是一个可怕的想法我错过了吗?

我考虑的其他选择

immutableMap.get(key) match {
    case Some(value) => value
    case None =>
        val value = <compute the value>
        immutableMap += key -> value
        value
}

没有太大的不同,而且更麻烦,或

if (immutableMap.contains(key)) {
    immutableMap(key)
} else {
    val value = <compute the value>
    immutableMap += key -> value
    value
}

这是最愚蠢的,可能是最不恰当的。

原则上我宁愿寻求使用帮助器来返回值和更新后的地图的解决方案,除非它是无可争议的优越方式。

2 个答案:

答案 0 :(得分:1)

当然,除了一个小问题似乎是合理的......它没有更新你的收藏!如果您正在使用不可变映射,那么该映射是不可变的。永远不能改变它。

事实上,Scala集合中的不可变Map甚至没有定义app.use(bodyParser.json({limit: '50mb'})); app.use(bodyParser.urlencoded({limit: '50mb', extended: true})); 方法,请参阅immutable.Map。使用&#34;追加&#34;的所有方法或者&#34;添加&#34; Map的新值实际上会返回 new Map。因此,对于您上面编写的内容进行编译,您不得不使用不可变的内容。

要使用不可变地图执行此操作,您需要使用+=并将var替换为新地图(这可能会导致线程问题)或者您有采用State Monad类型模式,不仅返回新值,还返回新Map。

var

答案 1 :(得分:0)

我唯一的建议(关于您选择var而不是mutable.Map或Java ConcurrentMap的原因)是将其包装到DSL中,例如:

case class Mutable[K,V](var m: Map[K,V]) {
   def orElseUpdate(key: K, compute: => V) = m.getOrElse(key, {
     val value = compute
     m += key -> value
     value
   })
}

scala> val a = Mutable(Map(1 -> 2))
a: Mutable[Int,Int] = Mutable(Map(1 -> 2))

scala> a.orElseUpdate(2, 4)
res10: Int = 4

scala> a.orElseUpdate(2, 6)
res11: Int = 4

scala> a.orElseUpdate(3, 6)
res12: Int = 6

另一个选项(如果你的计算是轻量级的)只是:

m += key -> m.getOrElse(key, compute)
m(key)

示例:

 scala> var m = Map(1 -> 2)
 m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)

 scala> m += 3 -> m.getOrElse(3, 5)

 scala> m
 res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)

 scala> m += 3 -> m.getOrElse(3, 5)

 scala> m
 res3: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)

 scala> m += 3 -> m.getOrElse(3, 6)

 scala> m
 res5: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)

您也可以将其包装到DSL中:

implicit class RichMap[K,V](m: Map[K,V]) {
  def kvOrElse(k: K, v: V) = k -> m.getOrElse(k, v)
}

scala> m += m.kvOrElse(3, 7)

scala> m
res7: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)

scala> m += m.kvOrElse(4, 7)

scala> m
res9: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5, 4 -> 7)