入出力の命令「.」と「,」を実装した。
最初,よく考えもせずに出力する関数 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番目)。
というわけで,何とかできたけど入出力は難しい。これで良いのかなぁ。もっとスマートにいかないものか。