1.帳簿をつける
担当者間でメッセージを送ることが可能になったので、帳簿をつけることにする。Haskellでのプログラムが、他の言語でのプログラムと異なる点は、変数の値を変えられないということである(STMは例外で、値の変更を安全におこなえる仕組みをもたせた)。
帳簿は、それぞれの勘定科目に現在高を示すので、トランザクションがあるたびに現在高を更新しなければならない。これはHaskellの基本的な考え方と根本的に異なる。値の変更をできるようにしたいと切望するであろうが、Haskellの世界ではかなわぬ夢と考えたほうが良い。
どうすれば解決するのか?トランザクションがあるごとに、現在の帳簿から、そのトランザクションを加えた新しい帳簿を作成することである。従って、会計担当者には、トランザクションがあるたびごとに、現在の帳簿とトランザクションの内容を知らせてあげる必要がある。このため、accountantの関数は、引数に帳簿(プログラムではbookになっている)をもつ。そして、accountantが一つのトランザクション処理を終了すると、再帰的にaccountantを呼び出し、新しい帳簿を渡してあげる。
2.プログラム
プログラムは以下に示すようになる。accountantの関数を簡単に説明しておく。最初にprintBookで現在の帳簿の内容をコンソールプロセスに送る(コンソールプロセスはこれを受けて出力する)。次に、購買担当者あるいは営業担当者から伝票(伝票といっても、購入したあるいは販売した商品の個数だけだが)が来ているかを調べる。もし、いずれかからきていれば、それに対する処理を行う。そうでなければ、伝票が来るのを待つ。
購買担当者から伝票が来ていれば、そのトランザクションの内容をコンソールプロセスに送り、さらに、新たな帳簿を作成し、それとともに、accountantを再帰的に呼び出す。
営業担当者から伝票が来た場合でも同じような処理を行う。
なお、コンソールへの出力を確認できるようにするために、購買担当者の購買間隔は5-10秒のランダムな時間に、営業担当者の販売間隔は2.5-5秒のランダムな時間に、変更した。
import Control.Concurrent.CHP import qualified Control.Concurrent.CHP.Common as CHP import Control.Concurrent.CHP.Console import Control.Monad import Control.Monad.Trans import System.Random main :: IO () main = do runCHP_(consoleProcess concurrentAgents) salesPerson :: Chanout Int -> CHP () salesPerson chanout = forever $ do writeC randomDelay where writeC = liftIO_CHP (getStdRandom (randomR (1,50))) >>= writeChannel chanout randomDelay = liftIO_CHP (getStdRandom (randomR (2500000,5000000))) >>= waitFor buyer :: Chanout Int -> CHP () buyer chanout = forever $ do writeC randomDelay where writeC = liftIO_CHP (getStdRandom (randomR (51,100))) >>= writeChannel chanout randomDelay = liftIO_CHP (getStdRandom (randomR (5000000,10000000))) >>= waitFor data Account = Account {cash :: Int, sales :: Int, purchasing :: Int, stock :: Int, goods :: Int} initBook = Account {cash = 100000, sales = 0, purchasing = 0, stock = 100000, goods = 0} acountant :: ConsoleChans -> Chanin Int -> Chanin Int -> Account -> CHP () acountant chans chaninS chaninB book = printBook book >> (readChannel chaninS >>= \y -> outProcessS book y) <-> (readChannel chaninB >>= \y -> outProcessB book y) where printBook :: Account -> CHP() printBook x = do printString ("Cash: " ++ show (cash x) ++ "\n") printString ("Sales: " ++ show (sales x) ++ "\n") printString ("Purchasing: " ++ show (purchasing x) ++ "\n") printString ("Stock: " ++ show (stock x) ++ "\n") printString ("Goods: " ++ show (goods x) ++ "\n") printString "\n" outProcessS :: Account -> Int -> CHP () outProcessS x y = printString ("New Sales:" ++ show y ++ "\n") >> acountant chans chaninS chaninB Account {cash = cash x + 100 * y , sales = sales x + 100 * y, purchasing = purchasing x, stock = stock x, goods = goods x - y} outProcessB :: Account -> Int -> CHP () outProcessB x y = printString ("New Purchasing:" ++ show y ++ "\n") >> acountant chans chaninS chaninB Account {cash = cash x - 80 * y , sales = sales x, purchasing = purchasing x + 80 * y, stock = stock x, goods = goods x + y} printString = mapM_(writeChannel (cStdout chans)) concurrentAgents :: ConsoleChans -> CHP () concurrentAgents chans = do s <- newChannel b <- newChannel runParallel_ [ salesPerson (writer s) , buyer (writer b) , acountant chans (reader s) (reader b) initBook]
3.問題
投資家をエージェントの一人として加えなさい。
4.問題
経理担当者が、一分ごとに、財務諸表、棚卸資産を報告するようにしなさい。