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-reset!"而不是"memo-swap!",所以命名不恰当。向后兼容性意味着我不能重命名它,但我可以添加"memo-reset!",还可以为"memo-swap!"引入一个新的arity来实现您期望的方式

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

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

至于swap! , vs. reset!的样式问题,我不确定为什么是这样写的。我会看看能否找出原因并回来告诉你。

by
现在主分支已经有这个更改了(memo-swap! 的额外参数!)

(memo/memo-swap! f 缓存/失配 参数 结果)

它还添加了 memo-reset! 功能,具有与 memo-swap! 相同的行为(并弃用了 memo-swap! 的2参数版本!)
by
这真是个绝妙的解决方案。我以为不可能在不依赖延迟实现细节的情况下添加通用的 "swap! cache-protocol-fn" 函数,但当然只有 miss 使用了三个参数。这是一个巧妙的解决方案。
0
by

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

然而,我认为正在进行的是多层的原子。出于某种奇怪的原因,core.cache 和 core.memoize 是不同的库,core.cache 尝试将缓存作为不可变的事物来展示,而 memoize 通过将缓存存储在原子中来添加可变性。但是随着开发的进行,很明显,“不可变”的缓存需要一些可变性,因此它们现在内部有自己的原子。所以现在的 memoize 代码似乎大部分忽略了 memoize 原子。

我认为这使其安全,即使看起来很糟糕。

by
clojure.core.cache仍然是全部不可变的。clojure.core.cache.wrapped最近被添加来提供包裹在一个原子中的缓存API版本,因为这基本上是人们使用它的方式。clojure.core.memoize始终将clojure.core.cache(不可变)缓存包裹在一个原子中,并存储在缓存函数的元数据中--但它也实现了clojure.core.cache协议,仿佛它也是一个不可变的缓存...这既有些困惑,也难以正确使用。

作为折中方案,clojure.core.memoize始终有一些在原子上操作的函数,但并不公开全部API...这也使得一些操作比实际需要的更难。

我认为,大多数情况下,clojure.core.memoize用户并不倾向于手动修改缓存的函数结果,所以这个用例从未被报告为需要(直到John最近发布的内容),因此这部分库之前并没有得到太多关注。
...