获取或更新的功能方式
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 方面,您可以使用 collectFirst
、getOrElse
之类的东西来使其在语法上更好,但问题仍然存在。
代码方面:
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
并检查是否需要更新缓存条目。
我有一个缓存,有两个函数可以从缓存中获取一个项目并将一个项目放入缓存。
当(从缓存中)获取项目时,如果键不存在,我需要为其填充一个值并 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 方面,您可以使用 collectFirst
、getOrElse
之类的东西来使其在语法上更好,但问题仍然存在。
代码方面:
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
并检查是否需要更新缓存条目。