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! 而不是reset!的样式问题,我不确定为什么它会这样写。我会看看是否能弄清楚这个问题,然后回你的邮件。

现在大师有这个修改了(memo-swap! 的额外参数)

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

它还添加了memo-reset!,具有memo-swap!的原始行为(并废弃了memo-swap!的2参数版本)。
这真是巧妙。我以为没有一种方法可以添加一个不依赖于延迟实现细节的“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,以提供包装在 atom 中的缓存 API 版本,因为这是人们使用它的主要方式。clojure.core.memoize 始终将 clojure.core.cache(不可变)的缓存包装在 atom 中,存储在 memoized 函数的元数据中——但同时也实现了 clojure.core.cache 协议,似乎它也是一个不可变的缓存...这既有些令人困惑,也极难正确使用。

作为一种妥协,clojure.core.memoize 也始终有一些操作那个 atom 的函数,但不公开完整的 API...这也使得一些操作比可能需要的更复杂。

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