事实上,您的余额都包含在一个单一的伞形数据结构下,并且该结构是一个适当的值,您的转移函数可以简单地接受描述帐户当前状态的结构,另一个描述变化的结构,并产生新的状态的帐户。这使您也可以将转移操作视为适当的值,这应该有助于处理非常长的转移列表:) 我唯一的改变是使用余额映射而不是列表。
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}}
接受来自客户的新转账的函数然后可以使用此函数来更改您选择将银行存储在其中的任何内容(数据库、原子、代理等)的状态
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])}>