残高がすべて単一の包括的なデータ構造に含まれており、その構造が適切な値であるという事実により、転送関数は、口座の現在の状態を記述する構造と、変化を記述する別の構造を単純に取り込み、新しい状態を生成することができます。アカウントの。これにより、転送アクションを適切な値として扱うことができ、転送の非常に長いリストを処理するのに役立ちます:)私の唯一の変更は、リストの代わりに残高のマップを使用することです.
bar> (def balances {"Steve" {:money 1000} "Bill" {:money 1000}})
#'bar/balances
bar> (def transfers [["Steve" "Bill" 100] ["Bill" "Steve" 100]
["Steve" "Bill" 10 ] ["Bill" "Steve" 10 ]
["Bill" "Steve" 10 ]])
#'bar/transfers
次に、これらのいずれかを取得してアカウントに適用する単純な伝達関数を定義します
(defn transfer [balances [from to ammount]]
(-> balances
(update-in [from :money] - ammount)
(update-in [to :money] + ammount)))
この関数は、すべてのアカウントの状態への転送のシーケンスを直接削減するために使用できます。
bar> (reduce transfer balances transfers)
{"Bill" {:money 990}, "Steve" {:money 1010}}
顧客からの新しい送金を受け入れる関数は、この関数を使用して、銀行を保存するために選択したもの (DB、atom、エージェントなど) の状態を変更できます。
bar> (def bank (agent {:current balances :ledger []}))
#'bar/bank
bar> (defn accept-transfers [transfers]
(send bank assoc :current (reduce transfer (:current @bank) transfers)
:ledger (concat transfers (:ledger @bank))))
#'bar/accept-transfers
bar> (accept-transfers transfers)
#<Agent@2eb9bc1: {:current {"Bill" {:money 1000}, "Steve" {:money 1000}}, :ledger []}>
これにより、銀行のキューに送金が置かれます (そして、送金が実行されている間に REPL が迅速に出力するエージェントを返します) 銀行を見ると、これらすべての送金が適用されていることがわかります。
bar> bank
#<Agent@2eb9bc1: {:current {"Bill" {:money 990}, "Steve" {:money 1010}},
:ledger (["Steve" "Bill" 100] ["Bill" "Steve" 100]
["Steve" "Bill" 10] ["Bill" "Steve" 10]
["Bill" "Steve" 10])}>