请在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

被选择
 
最佳答案

这是一个添加“memo-swap!”-样功能的良好论据

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

如该工单所述,“memo-swap!”被错误地命名,因为它实际上是“memo-reset!”。向后兼容性意味着我不能重命名它,但我可以添加“memo-reset!”以及引入对“memo-swap!”的新参数,后者会真正地表现出您想要的上述行为。

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

这将保持实现封装,并提供您想要的语义。

至于将 swap! ,,, constantly 而不是 reset! 的风格问题,我不确定为什么那样编写。我会看看是否能弄清楚并反馈给您。

现在主版本有了这个更改(memo-swap! 有一个额外的算子!)

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

它还添加了与 memo-swap! 相同的行为的 memo-reset!,并弃用了 memo-swap! 的二元版本!。
这真的很聪明。我以为没有一种方法可以添加一个“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 版本,因为这是大多数人使用它的方式。clojure.core.memoize 一直将 clojure.core.cache(不可变)缓存包装在原子中,存储在 memoize 函数的元数据中——但它也实现了 clojure.core.cache 协议,就像它也是一个不可变的缓存...这既有些令人困惑,又非常难以正确使用。

作为折衷,clojure.core.memoize 也始终有一些对原子操作的函数,但并未公开完整的 API...这也使得一些操作比实际需要的更难。

我认为,在很大程度上,clojure.core.memoize 的用户通常不想手动修改缓存的函数结果,因此这个用例从未报告为需求(直到最近 John 的线程),所以这个库的这一部分之前并没有太多关注。
...