逆ポーランド計算機
を作ってみた。
dcライクに"p"で表示〜とか考えてたのだが、どうも対話的に動かない。
とか思ってたらreadのせいっぽいね。
こんな感じのソース。
import Char(isSpace) dropSpace :: String -> String dropSpace = dropWhile isSpace data RPNData a = RPNNum a | RPNAdd | RPNSub | RPNMul | RPNDiv | RPNPrint instance Read a => Read (RPNData a) where readsPrec _ r = [(RPNNum x, dropSpace rs) | (x, rs) <- reads $ dropSpace r] ++ [(RPNAdd, dropSpace rs) | ("+", rs) <- lex $ dropSpace r] ++ [(RPNSub, dropSpace rs) | ("-", rs) <- lex $ dropSpace r] ++ [(RPNMul, dropSpace rs) | ("*", rs) <- lex $ dropSpace r] ++ [(RPNDiv, dropSpace rs) | ("/", rs) <- lex $ dropSpace r] ++ [(RPNPrint, dropSpace rs) | ("p", rs) <- lex $ dropSpace r] readList [] = [([], [])] readList r = [(x:xs, dropSpace rs) | (x, r1) <- reads $ dropSpace r , (xs, rs) <- reads r1] eval :: (Fractional a) => [RPNData a] -> IO [a] eval = eval' [] where eval' acc [] = return acc eval' acc (RPNNum x:xs) = eval' (x:acc) xs eval' (a:b:acc) (RPNAdd:xs) = eval' (b+a:acc) xs eval' (a:b:acc) (RPNSub:xs) = eval' (b-a:acc) xs eval' (a:b:acc) (RPNMul:xs) = eval' (b*a:acc) xs eval' (a:b:acc) (RPNDiv:xs) = eval' (b/a:acc) xs eval' acc@(a:_) (RPNPrint:xs) = print a >> eval' acc xs eval' _ _ = error "Stack Empty" main :: IO [Double] main = getContents >>= (eval . read)
readまわりがこれでいいのか分からんが動いてるからよしとしよう。
で、対話的に動かしたいのだがreadがあかん。
readListとかreadsPrecだけ見れば遅延評価がちゃんと働きそうなのだが、readが入力の遅延評価殺してて、結局'p'を入力した直後に"print a"が評価されてくれない...。
まぁ、あたりまえと言えば当たり前か...。
read "[1,2,3,4,5]" :: [Int]
とかやったとき、ほんとに[Int]として読めるかは']'まで(というか最後まで)読まないと分からない。
なので
head (read "[1,2,3,4,5]" :: [Int])
とかしても最後まで文字列は読まれてしまう...。
対話的にやりたいならgetLineでぐるぐる回せばいいのかな?
多分そんなことをしないといけんのじゃないかと思いつつ、とりあえずメモ書き。