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!... 持续被首选,或者它与 reset! 有什么不同,但我也没有维护者数十年的经验!

2 答案

+2 投票

已被选中
 
最佳答案

这是添加类似 "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! 缓存/失效 参数 结果)

它还添加了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,其中原子包裹的缓存版本,因为这是大多数人使用的方式。clojure.core.memoize 一直将 clojure.core.cache(不可变)的缓存包裹在原子中,存储在 memoized 函数的元数据中 —— 但它也实现了 clojure.core.cache 协议,就像它也是一个不可变缓存... 这既有些令人困惑,也难以正确使用。

作为一种折衷方案,clojure.core.memoize 一直有两个在上面的原子上操作的函数,但没有公开完整的 API... 这也使得一些操作比可能需要更难以简简单单地完成。

我认为,就大多数情况而言,clojure.core.memoize 的使用者不一定想手动修改缓存的函数结果,因此这种方法从未被认为是必需的(直到最近 John 的问题),所以这部分库一直没有得到太多关注。
...