2024 Clojure 状态调查 中分享您的想法!

欢迎!请参阅关于页面以了解更多关于如何使用本网站的信息。

+1 投票
core.memoize

虽然可以使用 memo-clear! 强制刷新,但我们是为了效率而缓存,所以能够提供一种手动设置缓存值的方法会很好。这是一种实施方式

(defn memo-miss!
  "Reaches into a core.memo-populated function and sets the cached value
  for a given seq of function args."
  [f args result]
  (when-let [cache (cache-id f)]
    (swap! cache clojure.core.cache/miss args (delay result))))

(testing "that setting cache values works as expected"
  (is (memo-clear! id))
  (is (memo-miss! id [29] :x))
  (is (= :x (id 29)))
  (is (= 42 (id 42))))

遵循现有的代码风格,这可以写作

(defn memo-miss!
  "Reaches into a core.memo-populated function and sets the cached value
  for a given seq of function args."
  [f args result]
  (when-let [cache (cache-id f)]
    (swap! cache
           (constantly (clojure.core.cache/miss @cache args (delay result))))))

我不理解为什么 swap! ... constantly 被频繁使用,或者它与 reset! 有何不同,但我也没有维护者的几十年经验!

2 答案

+2 投票

被 John Shaffer 选择
 
最佳答案

这为添加类似于 "memo-swap!" 的函数提供了很好的论据

https://clojure.atlassian.net/browse/CMEMOIZE-9

正如该票据所注,由于实际上是 "memo-reset!",因此清除 memo-swap! 的名称命名不当。向后兼容性意味着我无法重命名它,但我可以添加 memo-reset!并为 memo-swap! 引入一个新的参数,它真的以您期望的方式行事

(memo/memo-swap! f assoc args result)

这将保持实现封装并提供所需的语义。

至于将 swap! ,,, constantly 而不是 reset! 作为代码风格的问题,我不清楚为什么它是这样编写的。我会看看是否能弄清楚,然后回复您。

主分支已包含这个更改(在memo-swap!上增加了额外的一个arity)

(memo/memo-swap! f cache/miss args result)

此外,它还增加了memo-reset!,该函数具有与memo-swap!相同的原始行为(并废弃了memo-swap!的2-arity版本)。
这真的很有创意。我以为无法在不依赖于延迟实现细节的情况下添加一个通用的“swap! cache-protocol-fn”函数,但当然只有miss使用了3个参数。这是一个绝妙的解决方案。
0

我同意,在“constantly”中使用它是忽略了传入的缓存原子的值(如果函数被重试,这个值可能会改变),这使得它与reset!相同,意味着memo-miss!在那里引入了竞争条件。

然而,我认为这里是在多层的原子上运作。由于某种奇妙的原因,core.cache和core.memoize是不同的库,core.cache试图将缓存作为不可变的东西呈现,而memoize通过在原子中存储缓存来增加了可变性。但随着发展的进行,现在已经很明显,“不可变”的缓存需要一些可变性,因此它们现在内部有自己的原子。因此,memoize代码现在似乎主要忽略了memoize原子。

我认为这使得它变得安全,尽管看起来有点丑。

clojure.core.cache 仍然完全是不可变的。最近添加了 clojure.core.cache.wrapped 来提供 API 中封装在 atom 中的缓存版本,因为大多数人都是为了这个用途。clojure.core.memoize 一直把 clojure.core.cache(不可变)缓存封装在 atom 中,并存储在记忆化函数的元数据中--但它也像不可变缓存一样实现了 clojure.core.cache 协议...这既有些令人困惑,实际上也很难正确使用。

作为折衷方案,clojure.core.memoize 也一直有两个在 atom 上操作的函数,但并不暴露完整的 API,这也使得一些操作比实际上需要的更加复杂。

我认为,大部分情况下,clojure.core.memoize 用户并不倾向于手动修改缓存的函数结果,因此这种情况从未被报告为需求(直到最近 John 的帖子),所以这部分库一直没有得到很多关注。
...