gpt4 book ai didi

clojure - 仅在原子值更改时调用副作用函数

转载 作者:行者123 更新时间:2023-12-04 21:10:33 24 4
gpt4 key购买 nike

什么是触发副作用函数的最简单方法,仅当 atom 时才被调用?的值(value)变化?

如果我使用 ref ,我想我可以这样做:

(defn transform-item [x] ...)
(defn do-side-effect-on-change [] nil)

(def my-ref (ref ...))
(when (dosync (let [old-value @my-ref
_ (alter! my-ref transform-item)
new-value @my-ref]
(not= old-value new-value)))
(do-side-effect-on-change))

但这似乎有点迂回,因为我使用的是 ref即使我不想协调多个 ref 之间的更改s。本质上,我使用它只是为了方便地访问成功交易中的旧值和新值。

我觉得我应该可以使用 atom反而。有没有比这更简单的解决方案?
(def my-atom (atom ...))
(let [watch-key ::side-effect-watch
watch-fn (fn [_ _ old-value new-value]
(when (not= old-value new-value)
(do-side-effect-on-change)))]
(add-watch my-atom watch-key watch-fn)
(swap! my-atom transform-item)
(remove-watch watch-key))

这似乎也是迂回的,因为我在每次调用 swap! 时都会添加和删除 watch 。 .但我需要这个,因为我不希望 watch 卡在周围,导致在其他代码修改 atom 时触发副作用功能。 .

重要的是副作用函数在原子的每个突变中被调用一次,并且只有在变换函数 transform-item 时才被调用。实际上返回一个新值。有时它会返回旧值,产生新的变化。

最佳答案

(when (not= @a (swap! a transform))
(do-side-effect))

但是你应该很清楚你需要什么并发语义。例如,另一个线程可能会在读取和交换原子之间修改原子:
  • a = 1
  • 线程 1 将 a 读取为 1
  • 线程 2 将 a 修改为 2
  • 线程 1 将 a 从 2 交换到 2
  • 线程 1 确定 1 != 2 并调用 do-side-effect

  • 我不清楚这是可取还是不可取的问题。如果您不想要这种行为,那么除非您引入带锁的并发控制,否则原子将无法完成这项工作。

    当您从 ref 开始并询问原子时,我想您可能已经考虑过并发性。从您的描述看来, ref 方法更好:
    (when (dosync (not= @r (alter r transform))
    (do-side-effect))

    您是否有理由不喜欢您的 ref 解决方案?

    如果答案是“因为我没有并发”,那么我鼓励你无论如何都使用 ref。它没有真正的缺点,它使您的语义明确。 IMO 程序趋于增长并达到存在并发的程度,而 Clojure 非常擅长明确说明当它存在时应该发生什么。 (例如,哦,我只是在计算东西,哦,我现在只是将这些东西公开为 Web 服务,哦,现在我是并发的)。

    无论如何,请记住更改和交换之类的功能!返回值,因此您可以将其用于简洁的表达式。

    关于clojure - 仅在原子值更改时调用副作用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35544113/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com