Kotlin 中的记忆功能

问题描述 投票:0回答:4

我有一个现有的类,带有一个实例方法 buildHierarchyUncached,其签名可以在下面找到。

private fun buildHierarchyUncached(date: LocalDate): Node { ... }

我想提供一个公共 function buildHierchy,它是 buildHierarchyUncached 的记忆版本。我可以接近我想要的:

val buildHiearchy = Memoize<LocalDate, Node>({buildHierarchy(it)})

可以这样称呼:

hierarchyService.buildHiearchy(businessDate)

使用:

class Memoize<I, O>(val func: (I) -> O): (I) -> O{
  val cache = hashMapOf<I, O>();
  override fun invoke(p1: I): O {
    return cache.getOrPut(p1, { func(p1) } )
  }
}

我希望能够将记忆函数声明为函数而不是属性,这并不是什么大不了的事情,尽管我认为这有助于提高可读性。像这样:

fun buildHierarchy(date: LocalDate): Node = Memoize<LocalDate, Node>({ buildHierarchyUncached(it)})

但这不会编译:“类型不匹配。必需的节点。找到 memoize。”

另外,为什么不能编译?

val buildHiearchy = Memoize<LocalDate, Node>({(date) -> buildHierarchy(date)})
functional-programming kotlin
4个回答
10
投票

以下解决方案适用于单参数函数。如果您想创建该函数的缓存版本

bar
,您只需这样声明即可:

val cachedBar = makeFunctionCache({ bar(it) })

该实现将缓存存储在闭包中,这样您就不需要将其放在专用的类中:

fun <X, R> makeFunctionCache(fn: (X) -> R): (X) -> R {
    val cache: MutableMap<X, R> = HashMap()
    return {
        cache.getOrPut(it, { fn(it) })
    }
}

7
投票

根据问题的本质,您需要一个类字段来存储缓存(缓存值或缓存对象或委托)。所以你必须在类中的某个地方声明一个

val
,因为函数不能这样做。

请注意,当您声明

buildHiearchy
值时,您会得到两件事:将
Memoize<..>(..)
对象存储在类字段中,并获得
invoke()
函数(在其他地方声明,但仍然......)。我知道你没有办法在没有额外语法的情况下声明一个函数并获取字段存储。

代码片段使用过时的语法。像这样修复(无括号):

val buildHiearchy = Memoize<LocalDate, Node>({date -> buildHierarchy(date)})

7
投票

使用

Map.computeIfAbsent

val map = HashMap<LocalDate, Node>()
val key = LocalDate("Jun 14, 2022")

val value = map.computeIfAbsent(key) { 
    Node("My fancy node") 
}

计算仅执行一次并存储在

map


0
投票

这是 @Elias'es answer 的变体,带有函数参考扩展:

fun <X, R> ((X) -> R).memoized(): (X) -> R {
    val cache = mutableMapOf<X, R>()
    return { cache.getOrPut(it) { this(it) } }
}

fun <X, Y, R> ((X, Y) -> R).memoized(): (X, Y) -> R {
    val cache = mutableMapOf<Pair<X, Y>, R>()
    return { x, y -> cache.getOrPut(x to y) { this(x, y) } }
}

val loadLibraryMemoized = System::loadLibrary.memoized()
val concatMemoized = String::plus.memoized()
© www.soinside.com 2019 - 2024. All rights reserved.