在Clojure中,如果我记住一个函数,请将其命名为f
并在参数a
上调用它。
如果a
是一个很大的惰性值,那么memoize会根据匹配thunk而返回一个值,而不是强制评估a
并匹配结果吗?
如果thunk是懒惰序列的未评估部分。
如果不是这种情况,是否有内置的方法来获取此行为?
谢谢!
答案 0 :(得分:5)
Memoize将根据您传递给它的任何参数的值进行记忆。
因此 memoize可以使用延迟序列作为参数:因为它会查看序列的值,如果需要,它将强制评估 。
但这意味着你不能使用无限懒惰序列,因为使用memoize会强制评估它们,这显然不会很好地结束.....
答案 1 :(得分:5)
正如迈克拉所说,memoize
无法处理无限懒惰的序列。我正在添加这个答案,以提供对此实现原因的简短描述(加上两个基于身份的memoization方案的想法,一个简单,一个更复杂)。 (编辑:实际上有一个简单的基于身份的简单解决方案,见下文。)
memoize
使用哈希映射来存储从参数到值的映射,并且在确定对象是否是其中一个键时使用clojure.lang.Util/hasheq
(除了返回{{1}的空映射之外})。由于false
对lazy seqs的实现强制整个seq,如果无限懒惰seq是其中一个键,则询问任何映射将导致它进入一个无限的,耗尽内存的循环。因此hasheq
也是如此。
严格地说,最初地图是一个数组地图。 (在Clojure中,出于效率的原因,小地图通常是数组地图;如果有足够的项目memoize
'到数组地图上,则返回值将成为哈希映射。)但是非空数组映射由于涉及等价检查方法的类似原因,也无法处理无限的延迟seq。
assoc
返回System/identityHashCode
为给定对象返回的内容,如果它使用默认实现(无论是否覆盖其hashCode
)。
hashCode
现在你可以这样做了(这对普通的(defprotocol PWrapped
(-unwrap [this]))
PWrapped
(defn wrap [o]
(reify
Object
(hashCode [_] (System/identityHashCode o))
PWrapped
(-unwrap [_] o)))
;;; adapted from clojure.core/memoize, (C) Rich Hickey, EPL-licenced
(defn memoize
"Returns a memoized version of a referentially transparent function. The
memoized version of the function keeps a cache of the mapping from arguments
to results and, when calls with the same arguments are repeated often, has
higher performance at the expense of higher memory use."
{:added "1.0"
:static true}
[f]
(let [mem (atom {})]
(fn [& args]
(if-let [e (find @mem (map wrap args))]
(val e)
(let [ret (apply f args)]
(swap! mem assoc (map wrap args) ret)
ret)))))
不起作用):
memoize
关于替代实施的原始讨论如下。
我认为没有使用指针相等的内置解决方案。要实现像user> (def r (lazy-cat (range 10000) (prn :foo) (range)))
#'user/r
user> (def f (memoize #(apply + (take 100 %))))
#'user/f
user> (f [1 2 3])
6
user> (f r)
4950
这样的通用,您必须使用基于指针相等的散列(即memoize
)来实现映射结构。或者您可以使用System/identityHashCode
构建一个简单的“最近使用的”缓存。