スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#入門中級編第23回(Asynchronous Workflows(9) MailboxProcessor(4))

さてMailboxProcessorのまとめとして、id:suminさんが、どう書く?Orgに投稿された
居眠り床屋問題 http://ja.doukaku.org/285/
を取り上げてみたいと思います。
 
MailboxProcessorについては、今までに取り上げたメソッドの他に,CurrentQueueLengthプロパティを使用することにします。これはメッセージキューにいくつメッセージが残っているかを返します。
ではコードです。
 
open System
open System.Threading
 
type StylistAction =
| VisIDandTime of int*int  //客のIDと散髪所要時間
| FetchCutNumAndStop of AsyncReplyChannel<int> 
 
let stylist = new MailboxProcessor<StylistAction> (fun inbox ->
                let rec loop cutNum =
                  async{
                    let sleepState =
                        if inbox.CurrentQueueLength = 0 then //待っている客がいない
                            printfn "床屋、眠る"
                            true
                        else
                            false
                    let! msg = inbox.Receive()
                    match msg with
                       | VisIDandTime(id,t) ->
                            if sleepState then
                                printfn "床屋、目覚める"
                            printfn "散髪開始 %d" id
                            Thread.Sleep(t) //散髪所要時間
                            printfn "散髪完了 %d" id
                            return! loop (cutNum+1) 
                       | FetchCutNumAndStop(replyChannel) -> //終了時に送られてくる
                            printfn "床屋、眠る"
                            replyChannel.Reply(cutNum)
                            return ()
                       }
                loop 0)
 
 
let createVisAndIntervalAndSendVis (inStylist:MailboxProcessor<StylistAction>) allVisNum   =
   let rnd = new Random()
   let rec loop id =
       if id > allVisNum then
            let allCutNum = inStylist.PostAndReply(fun replyChannel -> FetchCutNumAndStop(replyChannel))
            printfn "%d人のうち %d人を散髪" allVisNum allCutNum
            ()
       else
         if id = 9 then
            Thread.Sleep(1200)
         else
            Thread.Sleep(rnd.Next(0, 200))
         
         if  inStylist.CurrentQueueLength > 1 then //Length=2 なら一人散髪中、二人待ち
            printfn "来店 %d\n満席で立ち去る %d" id id 
            loop (id + 1)
         else
            printfn "来店 %d " id 
            inStylist.Post(VisIDandTime(id,rnd.Next(100,400)))
            loop (id + 1)
                
   loop 1
 
stylist.Start()
createVisAndIntervalAndSendVis (stylist) 16
 
では実行してみます。
 
床屋、眠る
来店 1
床屋、目覚める
散髪開始 1
来店 2
散髪完了 1
散髪開始 2
来店 3
来店 4
来店 5
満席で立ち去る 5
来店 6
満席で立ち去る 6
来店 7
満席で立ち去る 7
散髪完了 2
散髪開始 3
来店 8
散髪完了 3
散髪開始 4
散髪完了 4
散髪開始 8
散髪完了 8
床屋、眠る
来店 9
床屋、目覚める
散髪開始 9
来店 10
来店 11
来店 12
満席で立ち去る 12
来店 13
満席で立ち去る 13
散髪完了 9
散髪開始 10
来店 14
来店 15
満席で立ち去る 15
散髪完了 10来店 16
満席で立ち去る 16
 
散髪開始 11
散髪完了 11
散髪開始 14
散髪完了 14
床屋、眠る
16人のうち 9人を散髪
 
ただ表示で「散髪完了 10」と改行の間に「来店 16」および「満室で立ち去る 16」が入りこんでますので、
dispAgentというMailboxProcessorを定義して、画面表示をこれに一本化したいと思います。改定版は次のようになります。
 
type DispAction =
| Str of string
| Stop of AsyncReplyChannel<unit> 
 
let dispAgent = MailboxProcessor<DispAction>.Start(fun inbox ->
                 let rec loop ()  =
                   async{
                    let! msg = inbox.Receive() 
                    match msg with
                    | Str(str) ->
                        printfn "%s" str
                        return! loop ()
                    | Stop(replyChannel) ->
                        replyChannel.Reply(())
                        return () 
                    }   
                 loop ())
 
 
type StylistAction =
| VisIDandTime of int*int  
| FetchCutNumAndStop of AsyncReplyChannel<int>
 
let stylist = new MailboxProcessor<StylistAction> (fun inbox ->
                let rec loop cutNum =
                  async{
                    let sleepState =
                        if inbox.CurrentQueueLength = 0 then
                            dispAgent.Post(Str("床屋、眠る"))
                            true
                        else
                            false
                    let! msg = inbox.Receive()
                    match msg with
                       | VisIDandTime(id,t) ->
                            if sleepState then
                                dispAgent.Post(Str( "床屋、目覚める"))
                            dispAgent.Post(Str("散髪開始 "+ id.ToString()))
                            Thread.Sleep(t)
                            dispAgent.Post(Str("散髪完了 "+ id.ToString()))
                            return! loop (cutNum+1) 
                       | FetchCutNumAndStop(replyChannel) ->
                            dispAgent.Post(Str("床屋、眠る"))
                            replyChannel.Reply(cutNum)
                            return ()
                       }
                loop 0)
 
 
let createVisAndIntervalAndSendVis (inStylist:MailboxProcessor<StylistAction>) allVisNum   =
   let rnd = new Random()
   let rec loop id =
       if id > allVisNum then
            let allCutNum = inStylist.PostAndReply(fun replyChannel -> FetchCutNumAndStop(replyChannel))
            let tempStr = sprintf "%d人のうち %d人を散髪" allVisNum allCutNum
            dispAgent.Post(Str(tempStr))
            let _ = dispAgent.PostAndReply(fun replyChannel -> Stop(replyChannel)) 
            ()
       else
         if id = 9 then
            Thread.Sleep(1200)
         else
            Thread.Sleep(rnd.Next(0, 200))
         
         if  inStylist.CurrentQueueLength > 1 then //Length=2 なら一人散髪中、二人待ち
            dispAgent.Post(Str("来店 "+ id.ToString()))
            dispAgent.Post(Str("満席で立ち去る "+ id.ToString()))
            loop (id + 1)
         else
            dispAgent.Post(Str("来店 "+ id.ToString()))
            inStylist.Post(VisIDandTime(id,rnd.Next(100,400)))
            loop (id + 1)
                
   loop 1
 
 
stylist.Start()
createVisAndIntervalAndSendVis (stylist) 16
 
実行例は次の通りです。
 
床屋、眠る
来店 1
床屋、目覚める
散髪開始 1
来店 2
来店 3
来店 4
満席で立ち去る 4
来店 5
満席で立ち去る 5
散髪完了 1
散髪開始 2
来店 6
来店 7
満席で立ち去る 7
来店 8
満席で立ち去る 8
散髪完了 2
散髪開始 3
散髪完了 3
散髪開始 6
散髪完了 6
床屋、眠る
来店 9
床屋、目覚める
散髪開始 9
来店 10
散髪完了 9
散髪開始 10
来店 11
来店 12
来店 13
満席で立ち去る 13
来店 14
満席で立ち去る 14
散髪完了 10
散髪開始 11
来店 15
来店 16
満席で立ち去る 16
散髪完了 11
散髪開始 12
散髪完了 12
散髪開始 15
散髪完了 15
床屋、眠る
16人のうち 9人を散髪
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。