入出力の命令「.」と「,」を実装した。
最初,よく考えもせずに出力する関数 bfPrint をこうした。
bfPrint bf = print $ bfValue bf
確かにこれでこの関数自体はちゃんと動く。つまり1文字出力される。
*Main> bfPrint $ bfIncrement bfInitial Loading package haskell98-1.0 ... linking ... done. 1
けど,返り値が IO () なのであとが続かない。出力命令が来たらそこで終わり,では話しにならないよな。
ここでしばらく行き詰まってしまった。
次の命令の処理につなげるには BrainF_ck を返さないといけないけど,どうやったらいいのか。
一部の命令だけ IO モナドになってしまうのを,他の命令と型を合わせるにはどうしたらいいのか。
結局「入門Haskell―はじめて学ぶ関数型言語」のモナドの章を読み直して,何とかできたのがこれ。
- 命令をつなげるのには IO BrainF_ck をつかう。
- 値を返すには return をつかう。
bfEvaluate は1つの命令を評価するようにして,次々に処理するのは main に移した。モナドを扱うので foldM を使った。
bfInput :: BrainF_ck -> IO BrainF_ck bfInput bf = do let p = bfPointer bf let r = bfRegister bf putStr "\ninput? " v <- getChar return $ BF p ((take p r) ++ [read [v]] ++ (tail $ drop p r)) bfPrint :: BrainF_ck -> IO BrainF_ck bfPrint bf = do putStr $ show $ bfValue bf return bf bfEvaluate :: BrainF_ck -> Char -> IO BrainF_ck bfEvaluate bf c = case c of '+' -> return $ bfIncrement bf '-' -> return $ bfDecrement bf '>' -> return $ bfShift bf '<' -> return $ bfUnshift bf '.' -> bfPrint bf ',' -> bfInput bf main :: IO () main = do args <- getArgs prog <- readFile $ head args result <- foldM bfEvaluate bfInitial prog print result
あ,foldM を使うには import Monad が必要。 さて,試してみよう。入力するプログラムはこれ。
++.>++.>++.<-.>>,.
結果。
>runghc hbf.hs sample.bf 2221 input? 7 7BF {bfPointer = 3, bfRegister = [2,1,2,7,0,0,0,0,0,0]}
一番最後に状態を出力してるから見にくいけどそれはおいといて。
input? のあとの 7 が入力。で,そのすぐあとに入力されたばかりの 7 を出力している。最後の状態を見てもちゃんと 7 が入力されている(左から4番目)。
というわけで,何とかできたけど入出力は難しい。これで良いのかなぁ。もっとスマートにいかないものか。