逆ポーランド電卓をつくってみました

ふつけるを7章くらいまで読み終えたので、この辺で一旦本から離れて自分でプログラムを書いてみました。僕は「新しい言語を覚えるときには逆ポーランド電卓から」と決めているので、今回も逆ポーランド電卓を作ってみました。うーむ、op_*関数のあたりとエラー処理がいまひとつおさまりが悪いです。

import System

main :: IO ()
main = do args <- getArgs
	  print $ rpcalc [] $ concatMap words args

rpcalc :: [Integer] -> [String] -> Integer
rpcalc (x:[]) [] = x
rpcalc _ [] = error "illegal expression."
rpcalc stack (x:xs)
    | x == "+"  = rpcalc (op_plus  stack) xs
    | x == "-"  = rpcalc (op_minus stack) xs
    | x == "*"  = rpcalc (op_mult  stack) xs
    | x == "/"  = rpcalc (op_div   stack) xs
    | otherwise = rpcalc ((strToInteger x) : stack) xs
    where
      op_plus :: [Integer] -> [Integer]
      op_plus (x1:x2:xs) = (x2 + x1) : xs
      op_plus _ = error "illegal expression(+)."
                  
      op_minus :: [Integer] -> [Integer]
      op_minus (x1:x2:xs) = (x2 - x1) : xs
      op_minus _ = error "illegal expression(-)."
                   
      op_mult :: [Integer] -> [Integer]
      op_mult (x1:x2:xs) = (x2 * x1) : xs
      op_mult _ = error "illegal expression(*)."
                  
      op_div :: [Integer] -> [Integer]
      op_div (0:_:_) = error "can't divid by 0."
      op_div (x1:x2:xs) = (x2 `div` x1) : xs
      op_div _ = error "illegal expression(/)."

      strToInteger :: String -> Integer
      strToInteger s = (read :: String -> Integer) s

以下、実行例です。

$ ./rpcalc.exe "1 2 + 4 * 2 /"
6

そうそう今さらながら気が付いたのですが、逆ポーランド記法って日本語と相性がよいですね。例えば、上の実行例の式は、「1と2を足して(+)、それに4を掛けて(*)、その結果を2で割る(/)」と読めます。日本語の語順です。
参照: id:ha-tan:20050914:1126649914