Clojure 仅当元素在集合中时才从集合中移除元素
玩家列表定义为一组:Clojure 仅当元素在集合中时才从集合中移除元素,clojure,Clojure,玩家列表定义为一组: (def players (atom #{})) 删除播放器的函数应根据元素是否在集合中返回不同的HTTP代码: (defn remove-player [player-name] (if (contains? @players player-name) (do (swap! players disj player-name) (status (response "") 200)) (status (response "") 404))
(def players (atom #{}))
删除播放器的函数应根据元素是否在集合中返回不同的HTTP代码:
(defn remove-player [player-name]
(if (contains? @players player-name)
(do (swap! players disj player-name)
(status (response "") 200))
(status (response "") 404)))
此代码的问题在于,它可能会向并发请求返回多个200
,即使只有一个请求实际删除了元素
我想我需要原子地执行
contains?
和disj
。我需要执行显式锁定还是有更好的方法?交换代码>本身是原子操作,因此在交换的计算函数中,可以确保第一个参数(原子的当前值)是一致的。就我个人而言,我会为其创建一个助手函数,如下所示:
(defn remove-existent [value set-a]
(let [existed (atom false)]
(swap! set-a
#(if (contains? % value)
(do (reset! existed true)
(disj % value))
(do (reset! existed false)
%))
@existed))
如您所见,lambda表达式包含存在性检查和删除
user> (def players (atom #{:user1 :user2}))
#'user/players
user> (remove-existent :user100 players)
false
user> (remove-existent :user1 players)
true
user> @players
#{:user2}
更新
受@clojuremostly优秀元数据方法的启发,您可以将其做得更好:
(defn remove-existent [value set-a]
(-> (swap! set-a #(with-meta (disj % value)
{:existed (contains? % value)}))
meta
:existed))
您只需向交换功能添加更多逻辑即可:
(for [el-rem [:valid-el :not-there]]
(let [a (atom #{:valid-el :another-one})
disj-res
(swap! a
(fn [a]
(with-meta (disj a el-rem)
{:before-count (count a)})))]
[disj-res
"removed:"
el-rem
(not (== (:before-count (meta disj-res))
(count disj-res)))]))
然后比较返回值
disj res
的计数和元数据中的计数。如果它不同,那么disj
确实删除了一个元素。如果不存在,则元素不存在。如果条件为“交换”,则可以移动它。swap代码>函数使包含?
和disj
调用原子。你的意思是我应该在交换之前保存当前计数,然后比较它们吗?它不是有同样的问题吗?差异可以是任何值(+100,-50,…)不,即使atom在您下面更改,swap当您disj
时,code>仍将是快照。因此,它是完全安全的,而且应该快2倍,因为您不必在它上面运行contains?
。谢谢,我不知道如何返回新集合和标志,您使用atom很好地解决了这个问题。更新了我的代码,将atom重置为false,如果该值不存在。我们真的需要重置它吗<代码>存在
是本地的,不是吗?是的,我们必须这样做。因为交换!可以重新启动多次,具体取决于其他线程对播放器的更改。因此,如果我们有player1,而另一个线程将其删除,则交换!将重新启动,并且需要刷新现有的
。我们将元数据附加到更新集并将其放入atom中,对吗?要读取现有的元数据,我们需要新的set对象,不是吗?