玩家列表被定义为一组:
(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
。我是否需要进行显式锁定或有更好的方法吗?
答案 0 :(得分:3)
swap!
本身是原子操作,所以在swap的计算函数中你可以确定第一个参数(atom的当前值)是一致的。就个人而言,我会为此做一个辅助函数:
(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))
答案 1 :(得分:2)
您可以为交换功能添加更多逻辑:
(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
确实删除了一个元素。如果没有,则该元素不存在。