State モナドの使い方がもうひとつよく分からなかったので、State Monad のチュートリアルを読んでいたら、Parsec と同じ使い方をすれば良いのだと気がついた。
Parsec ではパーサのモナドをプログラムしても、それだけでは何もしてくれない。たとえば、Parsec の(プリミティブ)関数の letter は、次のように parser 関数の引数にして実行しないとなにもしない。 Prelude Text.Parsec> parse letter "" "hello" Right 'h' また、要素的 (primitive) な関数はパーサーコンビネータと組み合わせることによって複雑なパターンに対応することができる。 Prelude Text.Parsec> parse (many1 letter) "" "hello world" Right "hello" さらに、パーサ関数はモナドなので、do 記法で結合することができる。 Prelude Text.Parsec> parse (do x <- char 'h'; y <- char 'e'; return [x,y]) "" "hello" Right "he" 同じような発想が、State モナドでも使える。 まず、State モナドの要素的な関数 (primitive) だが、return、put、get などがある。これらの関数は関数 runState の引数にすることで、状態を変化させ、runState はペア (値、状態)を戻り値として返す。 runState の型は次のようになる。 Prelude Control.Monad.State> :t runState runState :: State s a -> s -> (a, s) つまり State モナドと状態を引数にとり、(値、状態)のペアを戻り値として戻す。例えば要素的な State モナドである return を使ってみよう。return は単に値に引数を置くだけの動作をする。それでは return を使って 'X' を値として返し、状態は 1 を runState に渡してみる。 Prelude Control.Monad.State> runState (return 'X') 1 ('X',1) ちゃんと戻り値のペアの値のところに 'X' 状態のところに 1 が入れられて返る。このように、State モナドの分かりにくさは、return 単独では動作しないということだ。しかし、これも Parsec の使い方の類推で考えると途端にわかりやくすなる。 それでは、もう一つのプリミティブ get を調べてみよう。get は状態をとりだし、それを値にいれて返す。 Prelude Control.Monad.State> runState get 1 (1,1) また、put は状態に引数をいれ、値は () でリセットする。 Prelude Control.Monad.State> runState (put 2) 1 ((),2) また、これらの要素的なモナドは do 記法の中で組み合わせて使うことができる。次のプログラムは状態に5をセットし、'X' を値として返す。 Prelude Control.Monad.State> runState (do put 5; return 'X') 1 ('X',5) 次は状態を get で取り出し、その値に1を加えて状態に戻し、もとの状態を値として戻している。 Prelude Control.Monad.State> runState (do x <- get; put (x+1); return x) 1 (1,2) また modify モナドは状態を操作し、gets モナドは状態から取り出した値を加工して値に入れる。 Prelude Control.Monad.State> runState (modify (+1)) 1 ((),2) Prelude Control.Monad.State> runState (gets (+1)) 1 (2,1) さらに runState 関数と同じような使い方をするが、戻り値が値になる evalState と戻り値が状態になる execState がある。 Prelude Control.Monad.State> evalState (gets (+1)) 1 2 Prelude Control.Monad.State> execState (gets (+1)) 1 1 そこで、これらを使ってスタック操作をプログラムしてみた。 ファイル名: push.hs import Control.Monad.State push :: a -> State [a] a push x = do xs <- get; put (x:xs); return x pop :: State [a] a pop = do xs <- get; put (tail xs); return (head xs) 実行例: *Main> runState (do push 1; push 2; push 3) [] (3,[3,2,1]) *Main> runState (do push 1; push 2; push 3; pop; pop;) [] (2,[1]) ついでに、スタックを使って逆ポーランド記法で計算するプログラムを作ってみた。 ファイル名: calc.hs import Control.Monad.State push :: a -> State [a] a push x = do xs <- get; put (x:xs); return x pop :: State [a] a pop = do xs <- get; put (tail xs); return (head xs) poland "+" = do x <- pop; y <- pop; push (x + y) poland "*" = do x <- pop; y <- pop; push (x * y) poland x = push (read x :: Int) calc xs = evalState (do mapM_ poland xs; pop) [] 実行例: *Main> calc ["1","2","*"] 2 *Main> calc ["1","2","+","3","*"] 9
by tnomura9
| 2012-01-02 09:18
| Haskell
|
Comments(0)
|
カテゴリ
新型コロナウイルス 主インデックス Haskell 記事リスト 圏論記事リスト 考えるということのリスト 考えるということ ラッセルのパラドックス Haskell Prelude Ocaml ボーカロイド 圏論 jQuery デモ HTML Python ツールボックス XAMPP Ruby ubuntu WordPress 脳の話 話のネタ リンク 幸福論 キリスト教 心の話 メモ 電子カルテ Dojo JavaScript C# NetWalker ed と sed HTML Raspberry Pi C 言語 命題論理 以前の記事
最新のトラックバック
最新のコメント
ファン
記事ランキング
ブログジャンル
画像一覧
|
ファン申請 |
||