bitterharvest’s diary

A Bitter Harvestは小説の題名。作者は豪州のPeter Yeldham。苦闘の末に勝ちえた偏見からの解放は命との引換になったという悲しい物語

乱舞するメッセージ(2)

1.小さな会社

小さな会社の従業員は、購買担当者、営業担当者、経理担当者の3人とし、小さな会社は現金取引で商いをしているとする。購買担当者は仕入れをしたとき、経理担当者にその個数を伝えるものとし、販売担当者は販売をしたとき、経理担当者にやはりその個数を伝えるものとする。仕入れの単価は80円で、販売の単価は100円とする。経理担当者は、彼らからの伝達から会計用の帳簿を作るものとする。

2.エージェント間でのメッセージ・パッシング

とりあえず、購買担当者あるいは営業担当者から経理担当者へ、購入数あるいは販売数を伝えるために、この三人をプロセスとし、それぞれの間で、メッセージ・パッシングにより情報が伝達できるようにする。
f:id:bitterharvest:20140731065042p:plain
購買担当者あるいは営業担当者の仕事の様子は、乱数を用いてシミュレーションすることにする。即ち、購買担当者は、ランダムな時間間隔(0.5-1秒)でランダムな個数(51-100)の購入を行うとする。購買担当者は、ランダムな時間間隔(0.25-0.5秒)でランダムな個数(1-50)の購入を行うとする。
経理担当者は新たなトランザクションがあった時は、それをコンソールプロセスに伝え、コンソールプロセスは、その内容を表示するものとする。
以下がこのプログラムである。
このプログラムで気を付ける点は、乱数発生の部分である。乱数発生のための関数getStdRandomはIOモナドを出力する。しかし、このプログラムの世界はCHPモナドの世界なので、IOモナドからCHPモナドに移してあげる必要がある。これをしてくれるのが、liftIO_CHPである。その後、>>=になっているので、その値は、CHPモナドの世界から純粋な世界に戻され、writeChannelあるいはwaitForへの入力として与えられる。これらの結果は、再び、CHPモナドの世界である。
f:id:bitterharvest:20140731065331p:plain

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 = runCHP_(consoleProcess concurrentAgents)

sales :: Chanout Int -> CHP ()
sales chanout = 
  forever $ 
    do writeC
       randomDelay
    where
      writeC = liftIO_CHP (getStdRandom (randomR (1,50))) >>= writeChannel chanout 
      randomDelay = liftIO_CHP (getStdRandom (randomR (250000,500000))) >>= waitFor

purchasing :: Chanout Int -> CHP ()
purchasing chanout = 
  forever $ 
    do writeC
       randomDelay
    where
      writeC = liftIO_CHP (getStdRandom (randomR (51,100))) >>= writeChannel chanout 
      randomDelay = liftIO_CHP (getStdRandom (randomR (500000,1000000))) >>= waitFor

acountant :: ConsoleChans -> Chanin Int -> Chanin Int -> CHP ()
acountant chans chaninS chaninC = 
  forever $ 
    (readChannel chaninS >>= outProcessS) <-> (readChannel chaninC >>= outProcessC)
    where
      outProcessS :: Int -> CHP ()
      outProcessS x = printString ("Sales:" ++ show x  ++ "\n")
      outProcessC :: Int -> CHP ()
      outProcessC x = printString ("Purchasing:" ++ show x  ++ "\n")
      printString = mapM_(writeChannel (cStdout chans))

concurrentAgents :: ConsoleChans -> CHP ()
concurrentAgents chans = 
  do s <- newChannel
     p <- newChannel
     runParallel_
       [ sales (writer s)
       , purchasing (writer p)
       , acountant chans (reader s) (reader p)]