夢とガラクタの集積場

落ちこぼれ三流エンジニアである管理人の夢想=『夢』と、潰えた夢=『ガラクタ』の集積場です。

Clojure勉強日記(その19 アトムを使った非協調的、同期的な更新

こんにちは。では続きに入ります。

前回はrefを用いたSTMについて書いていましたが、Clojureには別の方式として「アトム」があります。

違いとしては・・下記です。
複数のrefの更新はトランザクションを使って協調させなければならなかった。
アトムは単一の値を、他の値とは関係なく更新するのに使える。

・・・とりあえず、実際のコードで確認してみましょうか。

user=> (doc atom)
-------------------------
clojure.core/atom
([x] [x & options])
  Creates and returns an Atom with an initial value of x and zero or
  more options (in any order):

初期化方法はrefと同じです。

user=> (def current-state (atom "INACTIVE"))
#'user/current-state
user=> @current-state
"INACTIVE"
; refでなくてAtomとして取得できる
user=> current-state
#<Atom@6b5cbaf2: "INACTIVE">
user=> (deref current-state)
"INACTIVE"

更新する際にはreset!を使用します。
ただ、refと違って複数の状態をまとめたトランザクションなどは実施できません。

user=> (reset! current-state "ACTIVE")
"ACTIVE"

そのため、複数の値を協調させたい場合はatomにマップを設定する形になります。

user=> (def current-message (atom {:sender "Sender1" :message "Message1"}))
#'user/current-message
user=> (reset! current-message {:sender "Sender2" :message "Message2"})
{:sender "Sender2", :message "Message2"}

マップ化した後に一部の値だけを設定したい場合はswap!とassocを使い、
元のマップの中身を入れ替えたものをatomとして再設定すればOKです。

user=> (swap! current-message assoc :message "Message3")
{:sender "Sender2", :message "Message3"}

・・・なんか、いまいち位置づけが中途半端で使いどころは微妙かもしれませんが。
後は次のエージェントが長いため、一度ここで切って次の記事へ。