2007-03-02
■ 逆ポーランド記法

はてなダイアリーで書いている方の日記で最近カロリー計算(ただ数字をたしているだけ)のためにてきとうなプログラムを書くというのをしているのですが、5日目にしてネタにつきたので、逆ポーランド記法の計算機をつくりました。
haskell taka$ cat rPo.hs rPo :: String -> Integer rPo str = evalPo [] $ words str where evalPo :: [Integer] -> [String] -> Integer evalPo l [] = head l evalPo l (x:xs) = if elem x exprs then flip evalPo xs $ flip evalExpr l $ getExpr x else flip evalPo xs $ flip (:) l $ strToInteger x exprs = [ "+" , "-" , "*" , "/" ] getExpr :: String -> ( Integer -> Integer -> Integer) getExpr x | x == "+" = (+) | x == "-" = (-) | x == "*" = (*) -- | x == "/" = (/) strToInteger = (read :: String -> Integer) evalExpr :: (Integer -> Integer -> Integer) -> [Integer] -> [Integer] evalExpr ex (x:y:xs) = flip (:) xs $ ex y x testData = [ ( rPo , "390 1042 + 191 204 + +" , 1827) , ( rPo , "365 328 + 210 + 191 + 577 90 + +" , 1761), ( rPo , "380 432 165 60 135 292 80 + + + + + +", 1544), ( rPo , "288 857 37 + + 185 + 204 + 85 +" , 1656), ( rPo , " 3 2 + 4 * 4 -" , 16)] testEql :: (Eq b) => [ ( ( a -> b) , a , b) ] -> [ (Bool,b,b) ] testEql = map (\(x,y,z) -> ((z ==( x y)) , (x y) , z))
文字列から数値の変換がわからなかったので趣味的にっきさんからstrToIntegerは拝借させていただきました。readでやるのね。
(/)をコメントアウトしているのは、(/)の型をまちがえてIntegerでつくってしまったため。ちょっと睡魔が襲ってきているので、起きたら対応するかも。
で、testEqlというテスト関数を作ってみた。関数とその引数と期待値のタプルを要素とした配列をもらい、その比較結果と値のタプル配列を返す。全部Trueなら成功。
実行結果はこちら
*Main> testEql testData [(True,1827,1827),(True,1761,1761),(True,1544,1544),(True,1656,1656),(True,16,16)]
最初は、最後のデータでFalseになった。理由はevalExprを
evalExpr ex (x:y:xs) = flip (:) xs $ ex x y
としてしまい、(False,-16,16)だって。スタックから数値を取り出して適用する順序を間違ってしまいました。
もっと、すっきり書けないかなぁ。
コメントを書く
Karime2012/05/05 10:44That's a smart answer to a tricky qstueion