-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrpn.hs
79 lines (64 loc) · 2.12 KB
/
rpn.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import Prelude hiding (splitAt)
import Control.Monad
import Control.Monad.State
import Control.Monad.Loops
import Debug.Trace
-- Folding
splitOn :: Char -> String -> [String]
splitOn c s = foldr woot [""] s
where woot x acc
| x == c = "" : acc
| otherwise = (x : head acc) : tail acc
calc :: (Fractional n, Read n) => [n] -> String -> [n]
calc (a : b : as) "+" = a + b : as
calc (a : b : as) "-" = a - b : as
calc (a : b : as) "*" = a * b : as
calc (a : b : as) "/" = a / b : as
calc as num = read num : as
rpn :: (Fractional n, Read n) => String -> n
rpn = head . foldl calc [] . splitOn ' '
-- Imperative
-- TODO: Write these next 3 imperatively
takeUntil :: (a -> Bool) -> [a] -> [a]
takeUntil f [] = []
takeUntil f (a : as) = if f a then [] else a : takeUntil f as
dropUntil :: (a -> Bool) -> [a] -> [a]
dropUntil f [] = []
dropUntil f l@(a : as) = if f a then l else dropUntil f as
splitAt :: (a -> Bool) -> [a] -> ([a],[a])
splitAt f as = let bs = takeUntil f as
cs = dropUntil f as
end = if length cs == 0 then [] else tail cs
in (bs, end)
type Env = State (String, [Double])
push :: Double -> Env ()
push v = do (str,vs) <- get
put $ (str,v : vs)
return ()
pop :: Env Double
pop = do (str, v : vs) <- get
put (str,vs)
return v
nextToken :: Env String
nextToken = do (str,vs) <- get
let (token, rest) = splitAt (== ' ') str
put (rest,vs)
return token
moreInput :: Env Bool
moreInput = do (str,_) <- get
return $ str /= ""
binOp :: (Double -> Double -> Double) -> Env ()
binOp f = do a <- pop
b <- pop
push $ f a b
rpn' :: String -> Double
rpn' str = (flip evalState) (str,[]) $ do
whileM_ moreInput $ do
token <- nextToken
case token of
"+" -> binOp (+)
"-" -> binOp (-)
"*" -> binOp (*)
"/" -> binOp (/)
num -> push $ read num
pop