有没有办法swap!
并返回任意值以及atom
的值?
例如:
(swap! (atom {1 1 2 2 3 3 4 4 5 5})
(fn [m]
(loop []
(let [i (rand-int 10)]
(if (contains? m i)
(recur)
(assoc m i "ok"))))))
在上面的函数中,我无法知道哪个键被添加到地图中(除非我事先列出它们)。
我可以使用另一个原子来存储结果,但它有一些我更容易忽略的原因吗?
答案 0 :(得分:3)
这是一种奇怪但通用的方法:
(defn swap-diff! [atom f & args]
(loop []
(let [curr-value @atom
new-value (apply f curr-value args)]
(if (compare-and-set! atom curr-value new-value)
[new-value (data/diff curr-value new-value)]
(recur)))))
这会在compare-and-set!
中使用loop
,直到成功为止,类似于how swap!
works internally。成功时,它返回新值的元组和clojure.data/diff
的输出。 diff
输出将准确显示原子值的变化情况。
(swap-diff! (atom {1 1, 2 2, 3 3})
#(loop []
(let [i (rand-int 10)]
(if (contains? % i)
(recur)
(assoc % i "ok")))))
=> [{1 1, 2 2, 3 3, 9 "ok"} (nil {9 "ok"} {3 3, 2 2, 1 1})]
{1 1, 2 2, 3 3, 9 "ok"}
是原子的新值。 (nil {9 "ok"} {3 3, 2 2, 1 1})
是diff
的输出,其中第一项是旧值中的仅项,第二项是新项 值,第三个值是两者中的项。在您的情况下,您只关心新项目。
更新:如果您不想处理元组返回值,可以使用diff元数据标记返回值:
(defn swap-diff! [atom f & args]
(loop []
(let [curr-value @atom
new-value (apply f curr-value args)]
(if (compare-and-set! atom curr-value new-value)
(with-meta new-value
(zipmap [:before :after :both]
(data/diff curr-value new-value)))
(recur)))))
然后在结果上调用meta
以获取diff:
(meta *1)
=> {:before nil, :after {9 "ok"}, :both {3 3, 2 2, 1 1}}
使用新功能swap-vals!
:
(defn swap-diff! [atom f & args]
(let [[old new] (apply swap-vals! atom f args)]
(with-meta new (zipmap [:before :after :both]
(data/diff old new)))))
答案 1 :(得分:1)
最简单的方法是将返回值和结果放在地图中:
(swap! (atom {:value {1 1 2 2 3 3 4 4 5 5}
:return nil})
(fn [{:keys [value]}]
(loop []
(let [i (rand-int 10)]
(if (contains? value i)
(recur)
{:value (assoc value i "ok")
:return i})))))
请注意,您的示例函数不是纯函数(因为它使用rand-int
),因此它不能正确使用swap!