too*_*ays 8 clojure memoization lazy-evaluation
在Clojure中,如果我记住一个函数,请将其命名f并在参数上调用它a.
如果a是一个大的延迟值,memoize是否会根据匹配thunk而返回一个值,而不是强制对结果进行评估a和匹配?
其中thunk是懒惰序列的未评估部分.
如果不是这种情况,有没有内置的方法来获得这种行为?
谢谢!
Memoize将根据您传递给它的任何参数的值进行记忆.
因此,memoize在延迟序列作为参数时可以正常工作:因为它会查看序列的值,如果需要,它将强制进行评估.
然而,这确实意味着你不能使用无限的懒惰序列,因为使用memoize会强制评估它们,这显然不会很好地结束.....
正如迈克拉所说,memoize不处理无限懒惰的seqs.我正在添加这个答案,以提供对此实现原因的简短描述(加上两个基于身份的memoization方案的想法,一个简单,一个更复杂).(编辑:实际上有一个简单的基于身份的简单解决方案,见下文.)
memoize使用哈希映射来存储从参数到值的映射,并clojure.lang.Util/hasheq在确定对象是否是其中一个键时使用哈希映射(刚刚返回的空映射除外false).由于hasheqlazy seqs的实现强制整个seq,如果无限懒惰seq是其中一个键,则询问任何映射将导致它进入一个无限的,耗尽内存的循环.因此同样如此memoize.
严格来说,最初地图是一个数组地图.(在Clojure中,出于效率的原因,小地图通常是数组映射;如果有足够的项目assoc进入数组映射,则返回值将成为哈希映射.)但是,非空数组映射也无法处理无限的延迟seqs到期涉及等价检查方法的类似原因.
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)))))
Run Code Online (Sandbox Code Playgroud)
现在你可以这样做了(这对普通人不起作用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
Run Code Online (Sandbox Code Playgroud)
以下是对替代实现的原始讨论.
我认为没有使用指针相等的内置解决方案.要实现一般memoize,你必须使用基于指针相等的散列(即System/identityHashCode)来实现映射结构.或者您可以使用构建一个简单的"最近使用的"缓存clojure.lang.PersistentQueue.
| 归档时间: |
|
| 查看次数: |
649 次 |
| 最近记录: |