Skip to content

Instantly share code, notes, and snippets.

@dvdblk
Last active January 16, 2017 14:36
Show Gist options
  • Save dvdblk/5a9a36a486bdc9bec87946c69e4c2e46 to your computer and use it in GitHub Desktop.
Save dvdblk/5a9a36a486bdc9bec87946c69e4c2e46 to your computer and use it in GitHub Desktop.
Haskell calculator
import Data.List
type Operator a = a -> a -> a
type Compute = [String] -> [String]
-- isDigit for strings from Data.Char
isDigit :: String -> Bool
isDigit [] = True
isDigit (x:xs) = (f >= 48) && (f <= 57) && isDigit xs where f = fromEnum x
charToOp :: Integral a => Char -> Operator a
charToOp '+' = (+)
charToOp '-' = (-)
charToOp '*' = (*)
charToOp '/' = div
charToOp '%' = mod
charToOp '^' = flip (^)
-- returns operation result as a string
operate :: Char -> String -> String -> String
operate op nr1 nr2 = show ((charToOp op) (read nr1 :: Int) (read nr2 :: Int)) :: String
-- returns a [String] with reduced size (calculates numbers for selected operator)
-- e.g ["2", "+", "3", "*", "4"] with operator '*' -> ["2", "+", "12"]
computeWithOp :: [String] -> String -> [String]
computeWithOp [x] _ = [x]
computeWithOp (nr1:op:nr2:s) y
| x `elem` y = computeWithOp ((operate (x) nr1 nr2):s) y
| otherwise = (nr1:op:(computeWithOp (nr2:s) y))
where x = head op
-- recursively applies x to a [String]
accMap :: ((Compute) -> [String]) -> [Compute] -> [String]
accMap f [x] = f x
accMap f (x:xs) = accMap ($ f x) xs
-- maps every operation (ordered by operator precedence) over the input [String]
-- we start with '^' (due to right associativity) and reverse the list when appropriate.
compute :: Compute
compute s = accMap ($ reverse (computeWithOp (reverse s) ['^'])) [ flip computeWithOp x | x <- [['*','/','%'], ['+','-']] ]
-- split to a [String] without blank spaces, then compute the result
eval :: String -> Int
eval = read . head . compute . words
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment