4

これについては、今日 IRC の#clojureチャンネルで少し話しましたが、ここでさらに詳しく説明したいと思います。swap!基本的に、アトム、 、および Clojure の同時実行性全体をよりよく理解するために、 を使用してスワップインされた値だけでなく、スワップアウトされた値もderef返す関数を書きたいと思います。swap!

(def foo (atom 42))

.
.
.

((fn [a]
  (do
    (println "swapped out: " @a)
    (println "swapped in: "(swap! a rand-int)))) foo)

印刷できます:

swapped out:  42
swapped in:   14

ただし、別のスレッドがと の呼び出しのswap!間で同じアトムを実行する場合、42 以外の値を交換している可能性があります。@a derefswap!

両方の値 (スワップアウトとスワップイン) を正しく返す関数を作成するにはどうすればよいですか?

アトムが変化するさまざまな値については気にしません。私が知りたいのは、交換された値が何であったかだけです。

これは、デッドロックしないことが保証されているコードを使用して記述できますか?また、そうである場合、その理由は?

4

6 に答える 6

16

Clojure のswap!ものは回転する比較と設定にすぎません。好きなものを返す代替バージョンを定義できます。

(defn alternate-swap [atom f & args]
  (loop []
    (let [old @atom
          new (apply f old args)]
      (if (compare-and-set! atom old new)
        [old new]  ; return value
        (recur)))))
于 2013-03-15T21:07:49.750 に答える
3

戻り値が必要な場合は、Stuart の回答が正しいですが、atom/ref がどのように機能するかを理解するために一連の println を実行する場合は、atom/ref http://にウォッチを追加することをお勧めします。 clojuredocs.org/clojure_core/1.2.0/clojure.core/add-watch

(add-watch your-atom :debug (fn [_ _ old new] (println "out" old "new" new)))
于 2013-03-16T07:53:32.430 に答える
3

アトムは調整されていないため、それ自体のスワッピング関数の外部でこれを実行しようとすると、失敗する可能性が高いと思われます。swap の代わりに呼び出す関数を作成できます。実際の関数を適用する前に既存の値を保存する関数を構築し、この構築された関数を に渡しますswap!

user> (def foo (atom  []))
#'user/foo
user> (defn save-n-swap! [a f & args] 
         (swap! a (fn [old-val] 
                    (let [new-val (apply f (cons old-val args))] 
                       (println "swapped out: " old-val "\n" "swapped in: " new-val) 
                       new-val)))) 
#'user/save-n-swap!
user> (save-n-swap! foo conj 4) 
swapped out:  [] 
 swapped in:  [4]
[4] 
user> (save-n-swap! foo conj 4) 
swapped out:  [4]
 swapped in:  [4 4]
[4 4] 

この例ではそれを出力します。別のアトムに保存されている変更ログにプッシュすることも理にかなっています

于 2013-03-15T20:43:01.527 に答える
2

次のようなマクロを使用できます。

(defmacro swap!-> [atom & args]
  `(let [old-val# (atom nil)
         new-val# (swap! ~atom #(do
                                  (swap! old-val# (constantly %))
                                  (-> % ~args)))]
     {:old @old-val# :new new-val#}))

(def data (atom {}))

(swap!-> data assoc :a 3001)
=> {:new {:a 3001} :old {}}
于 2013-03-16T08:11:12.020 に答える
0

操作内に現在の値を格納するという約束に頼ることができますswap!。次に、次のように新しい値と古い値をベクトルで返します。

(defn- swap-and-return-old-value!
  [^clojure.lang.IAtom atom f & args]
  (let [old-value-promise (promise)
        new-value (swap! atom
                     (fn [old-value]
                       (deliver old-value-promise old-value)
                       (apply f old-value args)))]
    [new-value @old-value-promise]))
于 2017-10-13T21:51:49.003 に答える