我如何在Clojure中分裂原子?

我如何在Clojure中分裂原子?,clojure,Clojure,我有一个存储在原子中的集合,就像这样 (def numbers (atom #{1 2 3 4 5})) 使用swap!定期在此集合中自动添加和删除数字。在另一个线程中,我希望有一个函数,可以从集合中提取和删除偶数并返回它们 我可以这样做的一种方法是: (let [{even true odd false} (group-by even? @numbers)] (reset! numbers odd) even) 然而,这不是一个原子操作编号可能会在分组之间通过和重置进行更改。有没有

我有一个存储在原子中的集合,就像这样

(def numbers (atom #{1 2 3 4 5}))
使用
swap!定期在此集合中自动添加和删除数字。在另一个线程中,我希望有一个函数,可以从集合中提取和删除偶数并返回它们

我可以这样做的一种方法是:

(let [{even true odd false} (group-by even? @numbers)]
  (reset! numbers odd)
  even)

然而,这不是一个原子操作<代码>编号
可能会在
分组之间通过
重置进行更改。有没有办法以原子方式执行此操作?

在这种情况下,维护原子性的惯用方法是使用事务:

(def numbers (ref #{1 2 3 4 5}))
(future (loop []
          (Thread/sleep 10)
          (dosync
           (alter numbers conj (rand-int 1000)))
          (recur)))

(dosync
 (let [{even true odd false} (group-by even? @numbers)]
   (ref-set numbers odd)
   even))
有关详细信息,请参阅:

编辑

如果您真的想使用原子,那么这将起作用:

(def numbers (atom #{1 2 3 4 5}))
(future (loop []
          (Thread/sleep 10)
          (swap! numbers conj (rand-int 1000)))
          (recur))

(loop []
  (let [val @numbers
        {even true odd false} (group-by even? val)]
    (if (compare-and-set! numbers val odd)
      even
      (recur))))

swap!应用的函数不需要是原子的。您应该可以使用
swap

如果您只想保留奇数,而要取回偶数,您可以通过
swap仅返回赔率,然后原子的值将更新为该值

剩下的问题是如何找回偶数。有很多方法可以做到这一点,我不知道什么是最好的。也许你可以在你的收藏中使用元数据?大概是这样的:

(def numbers (atom #{1 2 3 4 5}))

(defn atom-splitter [xs]
  (let [{even true odd false} (group-by even? xs)]
    (with-meta (set odd) {:evens (set even)})))

(swap! numbers atom-splitter)
@numbers
的值就是
{1 3 5}

(meta@numbers)
的值是
{:evens{24}

自从<代码>交换
返回交换的值,您可以确保返回值上的元数据包含拆分集合的另一半。例如,以下各项是安全的:

(defn split-off-evens [a]
  (-> (swap! numbers atom-splitter) meta :evens))
调用
(拆分偶数)
返回
{2 4}
,如果
数字
有其原始值。由于这仍然是对atom的正确使用,您可以假设Clojure处理所有线程安全问题。

同时,在2021年(自1.9起),您可以使用
交换VAL
以原子方式返回新旧值,因此evens当然只是它们之间的差异:

(apply clojure.set/difference
       (swap-vals! numbers
                   #(into #{} (filter odd? %))))

一般来说,让代码执行逻辑(例如将内容拆分)是不纯净的(任何了解引用类型而不是只处理数据的代码都是不纯净的)是一种味道。我认为这不是惯用的。当需要多个一致更新时,可以使用参照进行协调。然而,在这种情况下,仍然只有一个原子被更新。当您需要读-修改-写语义时,REF也是合适的。然而,它可以用原子来完成。请参阅更新的帖子。是的,refs可以为您提供一种安全的方式来执行读-修改-写操作,但这并不意味着您应该在现有的atom操作可以很好地完成任务时使用它们。我更喜欢你的新解决方案。你基本上只是。我想我可能更喜欢使用集合元数据的方法;使用元数据跳出框框思考的好方法。:)有趣的比较和设置!解决方案我喜欢这样,但是你不能在一个无限循环中结束吗?另外,谢谢,我需要更多地了解裁判。