获取或更新的功能方式

Functional way to get or update

我有一个缓存,有两个函数可以从缓存中获取一个项目并将一个项目放入缓存。

当(从缓存中)获取项目时,如果键不存在,我需要为其填充一个值并 return 该值。

以下是示例代码

class CacheComp {
    cache = Map[String, Foo]

    get(id): Foo = {
        // case(id exists) => Return matching Foo

        // case(id not exists) => Create a Foo and update the cache with created Foo. Then return updated Foo
    } 

    put(id, Foo) = {
        // put element to the cache   
    }
}

我在这里违反了 get(id) 函数的单一职责原则 (SRP)。如何在不违反 SRP 的情况下做到这一点?我可以简单地将函数重命名为 getOrUpdate(id)。但是有什么干净的函数式编程方法可以做到这一点吗?

你想要实现的是类似于一个幂等插入,即如果元素不存在则插入,如果存在return则现有对象的 ID。你必须记住,无论你如何命名函数,它仍然会对 cache 产生副作用。在 Scala 方面,您可以使用 collectFirstgetOrElse 之类的东西来使其在语法上更好,但问题仍然存在。 代码方面:

val cache = scala.collection.mutable.Map[String, Foo]()
def get(id: String): Foo = cache.collectFirst { case(key, foo) if key == id => foo } match {
  case Some(foo) => foo
  case None => {
    val foo = new Foo //dunno what it would be
    cache += (id -> foo)
    foo
  }
}

已在 mutable map 上定义了一个 getOrElseUpdate 函数。

val cache = scala.collection.mutable.Map[String, String]()
cache.getOrElseUpdate("lang", "scala")

如果您的目标是 'functional' 解决方案,您希望缓存 Map 是不可变的,因为在函数世界中一切都是不可变的。注意scala.collection.immutable.Map有这个方法:

override def updated [B1 >: B](key: A, value: B1): Map[A, B1]

现在有一个小问题 -- 更新地图后,如何使用具有更新值的缓存?您需要为此更改您的界面。

type Cache = Map[String, Foo]

object Cache {
  def get(id: String, cache: Cache): (Foo, Cache) = cache.get(id) match {
    case Some(e) => (e,cache)
    case None => 
      val foo = makeFoo
      (foo, cache.updated(id, foo))
  }

  def put(id: String, foo: Foo, cache: Cache): Cache = cache.updated(id, foo)
}

这为您提供了一个没有副作用的功能缓存。我还将进一步将 put 更改为 upsert 并检查是否需要更新缓存条目。