Haskellでdcっぽいのを作る その1
「Haskellやると彼女ができる」という都市伝説がある。
そういえばスタートHaskell第0回参加の時のアンケートで「なぜHaskellを始めたいか」って聞かれて「彼女ができると聞いて」って答えた気がする。そして私はHaskellを勉強しはじめてしまったので、今私に彼女ができたら「ホラやっぱりHaskell始めたからね」とか言われてしまうらしい。酷い話である。
なんか悔しいので関係ないけど「Haskell書くと痩せる」という都市伝説を作って放流しておきました。Pythonは痩せない。
そんなわけで、彼女を作るために日々頑張ってます。(Haskellを)
Haskellで彼女を作ればいいじゃん。(人工知能の本を開きながら)
まあどうでもいいや。
最近はErlangで遊んでたんですけど、やっぱりちょっとHaskell書いてみたくなって戻って来ました。というのが本当のところです。
あ、今は酒飲んでません。
で、できたのがこれです。
dc_haskell at master from yunomu/exercises - GitHub
UNIXのdcコマンドっぽいのを作りたかった。いわゆる逆ポーランド記法の電卓です。
逆ポーランド記法だと、演算子順位構文解析なんてしなくていいし、スタックがあれば作れるので楽かなぁと思って。使いやすいですしね。なんで逆ポーランド記法の電卓ってあんまし売ってないんですかね。少なくとも日本ではもっと普及しててもいいはず。だって「1と2を足してそれに3をかける」が「1,2,+,3,*」と書ける……だとあんまし普通の電卓と変わりませんけど、「1と2に3をかけたものを足す」が「1,2,3,*,+」って書けるんですよ。日本語のまんまじゃないですか。
話がそれましたが、
電卓を作りました。電卓は入出力とか文字列処理とか計算とかがひと通りあるので、Webサーバに並んでお勉強用に実装する事が多いシステムです。いろんな言語で何度も実装してきましたが、こんなに手こずったのは最初のCの時以来かもしれません。
とりあえずparseを使ってみたかったので「プログラミングHaskell」からparserの一部をコピペしてParserモジュールを作りました。とはいえ単に整数を読むために使っているだけなので、dc.hsの中のparse int xsの部分は実はread xsとかでもいいです。
calc :: Stack Int -> String -> Stack Int calc s xs | xs == "+" = ope s (+) | xs == "-" = ope s (-) | xs == "*" = ope s (*) | xs == "/" = ope s div | otherwise = case parse int xs of [(v, "")] -> push s v _ -> error "invalid input."
ここを
calc :: Stack Int -> String -> Stack Int calc s xs | xs == "+" = ope s (+) | xs == "-" = ope s (-) | xs == "*" = ope s (*) | xs == "/" = ope s div | otherwise = push s (read xs)
こんな感じ?
あとエラーのハンドリングがうまくいってなくて、変な文字列を入れた時のエラー処理がpとかqを実行するまで表にでてこないという。直し方はわかるんだけど、なんでそう動くのかよくわかんなくて、ちょっと放置している。
それととりあえず適当にって感じのopeが
ope :: Stack Int -> (Int -> Int -> Int) -> Stack Int ope s f = push s2 (f v1 v2) where (v1, s1) = pop s (v2, s2) = pop s1
これなんかかっこ悪い気がする。こっちもエラー飛ばないし。そもそもStackを
data Stack a = Stack [a]
って定義してるのも、これもなんか本当にこれでいいのかなぁという感じ。よくわかんないんですけど。
あとインタプリタだと動くけど、コンパイルするとプロンプトが出ない。出ないほうがdcっぽいから消しちゃうという手もあるんだけど、また例によってpとかqを実行するまでプロンプトの出力が遅延されてる。
やはりモナドか。遅延評価はある意味厄介というか、意識しないとうまく動きませんね。
なんかスッキリしないのでなかなかErlangに行けない。
今回は酒飲んでません。