Reverse Polish notation calculator in Haskell
Just a few lines of code, written when I was slightly bored.
module Main where data Token = Plus | Minus | Divide | Times | Num Integer rpnEval :: [Token] -> Integer rpnEval = rpnEval' [] where rpnEval' (i:s) [] = i rpnEval' s ((Num i):t) = rpnEval' (i:s) t rpnEval' (a:b:s) (op:t) = rpnEval' ((evalOp op a b):s) t evalOp Plus = (+) evalOp Minus = (-) evalOp Times = (*) evalOp Divide = div toToken :: String -> Token toToken "+" = Plus toToken "-" = Minus toToken "*" = Times toToken "/" = Divide toToken s = Num (read s) main = do l <- getContents mapM print $ map (rpnEval . (map toToken) . words) (lines l)
Update: See comments for better solution.
Nice:) A bit shorter version:
module Main where
data Token = Binary (Integer -> Integer -> Integer) | Num Integer
rpnEval :: [Token] -> Integer
rpnEval = rpnEval’ [] where
rpnEval’ (i:s) [] = i
rpnEval’ s ((Num i):t) = rpnEval’ (i:s) t
rpnEval’ (a:b:s) ((Binary op):t) = rpnEval’ ((op a b):s) t
toToken :: String -> Token
toToken “+” = Binary (+)
toToken “-” = Binary (-)
toToken “*” = Binary (*)
toToken “/” = Binary div
toToken s = Num (read s)
main = do
l <- getContents
mapM print $ map (rpnEval . (map toToken) . words) (lines l)
Thanks. :)
Great! Thank you very much!
I always wanted to write in my site something like that. Can I take part of your post to my site?
Of course, I will add backlink?
Regards, Timur I. Alhimenkov
You’re very welcome. All my source codes are under GPL and blog content under Creative Commons BY-SA.
Those lines:
> main = do
> l mapM print $ map (rpnEval . (map toToken) . words) (lines l)
Can be shortened to:
> main = interact (unlines . map (rpnEval . (map toToken) . words) . lines))
Haskell FTW! :-)
Dzięki za przypomnienie o ,,interact”. ;) Oczywiście jeszcze po ,,rpnEval” trzeba zrobić ,,show”, bo ,,unlines” bierze listę napisów, a nie liczb.
Wikipedia gives on http://en.wikipedia.org/wiki/Haskell_features#More_complex_examples version using fold:
calc :: String -> [Float]
calc = foldl f [] . words
where
f (x:y:zs) “+” = (y + x):zs
f (x:y:zs) “-” = (y – x):zs
f (x:y:zs) “*” = (y * x):zs
f (x:y:zs) “/” = (y / x):zs
f xs y = read y : xs
Using a view pattern you can obtain a minimalistic version:
{-# LANGUAGE ViewPatterns #-}
module Main where
main = interact $ unlines . map (show . head . foldl f [] . words) . lines
where
f (x:y:zs) (operator -> Just o) = (o x y):zs
f xs y = read y : xs
operator o = lookup o [("+",(+)),("-",(-)),("*",(*)),("/",div)]