1
+ module GraphCalc where
2
+
1
3
import Test.QuickCheck
2
4
import Parsing
3
5
import Data.Maybe
4
6
import Data.Char
5
7
import Data.List
6
8
import Control.Monad
7
9
10
+
11
+ data Expr = Num Double
12
+ | MonoOp MonoFunc Expr
13
+ | BinOp BinFunc Expr Expr
14
+ | Var
15
+ deriving (Eq , Show )
16
+
17
+ data MonoFunc = Sin | Cos
18
+ deriving (Eq , Show )
8
19
9
- data Expr = Num Double
10
- | Var Char
11
- | BinOp String (Double -> Double -> Double ) Expr Expr
12
- | MonoOp String (Double -> Double ) Expr
20
+ data BinFunc = Add | Mul
21
+ deriving (Eq , Show )
13
22
14
23
15
- instance Show Expr
16
- where show = showExpr
24
+ -- instance Show Expr
25
+ -- where show = showExpr
26
+
27
+ instance Arbitrary Expr where
28
+ arbitrary = sized arbExpr
17
29
18
30
showExpr :: Expr -> String
19
- showExpr (Num n) = show n
20
- showExpr ( Var _) = " x"
21
- showExpr (MonoOp name _ expr ) = name ++ " " ++ case expr of
22
- ( Num n) -> showExpr expr
23
- ( Var _) -> showExpr expr
24
- _ -> " ( " ++ showExpr expr ++ " ) "
25
- showExpr ( BinOp name _ expr1 expr2) = case name of
26
- " * " -> showFactor expr1 ++ " " ++ name ++ " " ++ showFactor expr2
27
- " + " -> showExpr expr1 ++ " " ++ name ++ " " ++ showExpr expr2
28
- where
29
- showFactor ( BinOp " + " _ expr1 expr2) = " ( " ++ showExpr (add expr1 expr2) ++ " ) "
30
- showFactor expr = showExpr expr
31
+ showExpr (Num n) = show n
32
+ showExpr Var = " x"
33
+ showExpr (BinOp Mul e1 e2 ) = showFactor e1 ++ " * " ++ showFactor e2
34
+ showExpr ( BinOp Add e1 e2) = showExpr e1 ++ " + " ++ showExpr e2
35
+ showExpr ( MonoOp Sin e) = " sin " ++ showArg e
36
+ showExpr ( MonoOp Cos e) = " cos " ++ showArg e
37
+
38
+ showFactor ( BinOp Add e1 e2) = " ( " ++ showExpr (add e1 e2) ++ " ) "
39
+ showFactor e = showExpr e
40
+ showArg ( BinOp op e1 e2) = " ( " ++ showExpr ( BinOp op e1 e2) ++ " ) "
41
+ showArg e = showExpr e
42
+
31
43
32
44
--------------------------------------------------------
33
45
34
46
x :: Expr
35
- x = Var ' x '
47
+ x = Var
36
48
37
49
num :: Double -> Expr
38
50
num d = Num d
39
51
40
52
add ,mul :: Expr -> Expr -> Expr
41
- add x y = BinOp " + " (+) x y
42
- mul x y = BinOp " * " (*) x y
53
+ add x y = BinOp Add x y
54
+ mul x y = BinOp Mul x y
43
55
44
56
sin ,cos :: Expr -> Expr
45
- sin x = MonoOp " sin " Prelude. sin x
46
- cos x = MonoOp " cos " Prelude. cos x
57
+ sin x = MonoOp Sin x
58
+ cos x = MonoOp Cos x
47
59
48
60
----------- TEST DATA ---------------------------
49
61
50
- test = BinOp " +" (+) (Num 1 ) (Num 2 ) -- = 3
51
- testf = MonoOp " sin" Prelude. sin (Num 0 ) -- = 0
52
- test2 = MonoOp " sin" Prelude. sin test -- = sin(3) ca 0.14
62
+ test = BinOp Add (Num 1 ) (Num 2 ) -- = 3
63
+ testf = MonoOp Sin (Num 0 ) -- = 0
64
+ test2 = MonoOp Sin test -- = sin(3) ca 0.14
65
+
66
+ test3 = mul (add (Num 1 ) (Num 2 )) (add (Num 3 ) (Num 5 )) -- = 3 * 8 = 24
67
+ test4 = add (add (Num 1 ) (Num 2 )) (add (Num 3 ) (Num 5 )) -- = 3 + 8 = 11
68
+ test5 = mul (mul (Num 1 ) (Num 2 )) (mul (Num 3 ) (Num 5 )) -- = 1*2 * 3*5 = 2 * 15 = 30
53
69
54
- test3 = mul (add (Num 1 ) (Num 2 )) (add (Num 3 ) (Num 5 )) -- = 3 * 8 = 24
55
- test4 = add (add (Num 1 ) (Num 2 )) (add (Num 3 ) (Num 5 )) -- = 3 + 8 = 11
56
- test5 = mul (mul (Num 1 ) (Num 2 )) (mul (Num 3 ) (Num 5 )) -- = 1*2 * 3*5 = 2 * 15 = 30
70
+ test6 = mul (mul (Var ) (Num 2 )) (mul (Num 3 ) (Num 5 )) -- = 1*2 * 3*5 = 2 * 15 = 30
57
71
58
- test6 = mul (mul (Var ' x' ) (Num 2 )) (mul (Num 3 ) (Num 5 )) -- = 1*2 * 3*5 = 2 * 15 = 30
72
+ qFail = MonoOp Cos (MonoOp Cos (BinOp Mul Var (Num (- 4.087662660418292 ))))
73
+ failt = BinOp Mul (MonoOp Cos (BinOp Mul Var (Num 1 ))) (Num 2 )
59
74
60
75
--------------------------------------------------
61
76
62
77
-- | Evaluates a given expression, where the second argument is the value of x
63
78
eval :: Expr -> Double -> Double
64
- eval ( Var _) x = x
79
+ eval Var x = x
65
80
eval (Num n) _ = n
66
- eval (BinOp _ op i j) x = (eval i x) `op` (eval j x)
67
- eval (MonoOp _ op i) x = op (eval i x)
81
+ eval (BinOp Add i j) x = (eval i x) + (eval j x)
82
+ eval (BinOp Mul i j) x = (eval i x) * (eval j x)
83
+ eval (MonoOp Cos i) x = Prelude. cos (eval i x)
84
+ eval (MonoOp Sin i) x = Prelude. sin (eval i x)
68
85
69
86
-- | Tries to read a expression from a string
70
87
readExpr :: String -> Maybe Expr
71
- readExpr s = undefined
72
-
73
- {- }
74
- do
75
- let s' = filter (not . isSpace) s
76
-
77
- -}
78
-
88
+ readExpr s = case parse expr s' of
89
+ Just (ex," " ) -> return ex
90
+ _ -> Nothing
91
+ where s' = filter (not . isSpace) s
92
+
93
+
94
+ assoc :: Expr -> Expr
95
+ assoc (BinOp Add e1 (BinOp Add e2 e3)) = assoc (add (add (assoc e1) (assoc e2)) (assoc e3))
96
+ assoc (BinOp Add e1 e2) = add (assoc e1) (assoc e2)
97
+ assoc (BinOp Mul (BinOp Mul e1 e2) e3) = assoc (mul (mul (assoc e1) (assoc e2)) (assoc e3))
98
+ assoc (BinOp Mul e1 e2) = mul (assoc e1) (assoc e2)
99
+ assoc (MonoOp op e) = MonoOp op (assoc e)
100
+ assoc (Num n) = Num n
101
+ assoc Var = Var
102
+
103
+ ------------- PARSERS------------------------
79
104
-- | Parse a number
80
- number :: Parser Double
81
- number = read <$> oneOrMore digit
105
+ number :: Parser Expr
106
+ number = Num <$> readsP
82
107
83
108
-- | Parse an x-variable
84
- xparse :: Parser Char
85
- xparse = char ' x'
86
-
109
+ variable :: Parser Expr
110
+ variable = char ' x' *> return Var
111
+
112
+ -- | Parse the sin function
113
+ sinparse :: Parser Expr
114
+ sinparse = do f <- funcparse ' s' ' i' ' n' Sin
115
+ e <- factor
116
+ return (MonoOp f e)
117
+ -- | Parse the cos function
118
+ cosparse :: Parser Expr
119
+ cosparse = do f <- funcparse ' c' ' o' ' s' Cos
120
+ e <- factor
121
+ return (MonoOp f e)
122
+
123
+ -- | Simple parser for a three letter function
124
+ funcparse a b c f = do c1 <- char a
125
+ c2 <- char b
126
+ c3 <- char c
127
+ return f
128
+ cosine :: Parser Expr
129
+ cosine = ((MonoOp Cos ) <$> k)
130
+ where k = (char ' c' ) *> (char ' o' ) *> (char ' s' ) *> factor
131
+
132
+ sine :: Parser Expr
133
+ sine = ((MonoOp Sin ) <$> k)
134
+ where k = char ' s' *> char ' i' *> char ' n' *> factor
135
+
136
+ -- | Parses expressions
87
137
expr , term , factor :: Parser Expr
88
138
expr = foldl1 add <$> chain term (char ' +' )
89
- term = foldl1 mul <$> chain factor (char ' *' )
90
- factor = Num <$> number <|> (Var <$> xparse <|> char ' (' *> expr <* char ' )' )
91
-
92
-
93
-
139
+ term = foldl1 mul <$> chain factor (char ' *' )
140
+ factor = number <|> variable <|> char ' (' *> expr <* char ' )' <|> sinparse <|> cosparse
141
+
142
+ -- | Test that showExpr and readExpr produces the same result
143
+ prop_ShowReadExpr :: Expr -> Bool
144
+ prop_ShowReadExpr e = (assoc $ fromJust $ readExpr (showExpr e)) == assoc e
145
+
146
+ -- | Generator for arbitrary expressions
147
+ arbExpr :: Int -> Gen Expr
148
+ arbExpr n = frequency [(1 ,rNum),(1 ,rVar),(n,rBin),(n,rMon)]
149
+ where
150
+ range = 5
151
+ rVar = return Var
152
+ rNum = Num <$> choose (- range,range)
153
+ rBin = do
154
+ let n' = n `div` 2
155
+ op <- oneof [return add, return mul]
156
+ e1 <- arbExpr n'
157
+ e2 <- arbExpr n'
158
+ return $ op e1 e2
159
+ rMon = do
160
+ op <- oneof [return GraphCalc. sin , return GraphCalc. cos ]
161
+ e <- arbExpr (n- 1 )
162
+ return $ op e
163
+
164
+ -- | Simplifies expressions
165
+ simplify :: Expr -> Expr
166
+ simplify (Num n) = Num n
167
+ simplify (Var ) = Var
168
+ simplify (BinOp Add x y) = simplifyAdd (simplify x) (simplify y)
169
+ simplify (BinOp Mul x y) = simplifyMul (simplify x) (simplify y)
170
+ simplify (MonoOp f x) = simplifyFunc f (simplify x)
171
+
172
+ -- | Simplifies multiplication expressions
173
+ simplifyMul :: Expr -> Expr -> Expr
174
+ simplifyMul (Num 0 ) e = Num 0
175
+ simplifyMul e (Num 0 ) = Num 0
176
+ simplifyMul e (Num 1 ) = e
177
+ simplifyMul (Num 1 ) e = e
178
+ simplifyMul x y = mul (simplify x) (simplify y)
179
+
180
+ -- | Simplifies add expressions
181
+ simplifyAdd :: Expr -> Expr -> Expr
182
+ simplifyAdd e (Num 0 ) = simplify e
183
+ simplifyAdd (Num 0 ) e = simplify e
184
+ simplifyAdd x y = add (simplify x) (simplify y)
185
+
186
+ -- | Simplifies sin and cos expressions
187
+ simplifyFunc :: MonoFunc -> Expr -> Expr
188
+ simplifyFunc Sin (Num x) | x == 0 || x == pi = Num 0
189
+ | x == (pi / 2 ) = Num 1
190
+ simplifyFunc Sin e = MonoOp Sin (simplify e)
191
+ simplifyFunc Cos (Num x) | x == 0 || x == pi = Num 1
192
+ | x == (pi / 2 ) = Num 0
193
+ simplifyFunc Cos e = MonoOp Cos (simplify e)
194
+
195
+ -- | Test that a simplified expression returns correct result
196
+ prop_Simplify :: Expr -> Bool
197
+ prop_Simplify e = (eval e 0 ) == (eval (simplify e) 0 )
198
+
199
+ -- | Differentiates an expression with regard to x
200
+ differentiate :: Expr -> Expr
201
+ differentiate (Num _) = Num 0
202
+ differentiate (Var ) = Num 1
203
+ differentiate (BinOp Mul Var Var ) = simplify $ mul (Num 2 ) Var
204
+ differentiate (BinOp Mul e1 e2) = simplify $ add (mul (differentiate e1) e2) (mul e1 (differentiate e2))
205
+ differentiate (BinOp Add e1 e2) = simplify $ add (differentiate e1) (differentiate e2)
206
+ differentiate (MonoOp Sin e) = simplify $ mul (differentiate e) (MonoOp Cos e)
207
+ differentiate (MonoOp Cos e) = simplify $ mul (Num (- 1 )) (mul (differentiate e) (MonoOp Sin e))
208
+
209
+
210
+
211
+
212
+
213
+ ----------- MAIN-----------------------------
94
214
main = do putStrLn " Welcome to the simple calculator!"
95
- readEvalPrint = do
96
- putStr " What would you like to calculate?"
97
- s <- getLine
98
- let s' = filter (not . isSpace) s
99
- case parse expr s' of
100
- Just (e, " " ) -> print $ eval e 1
101
- Nothing -> putStrLn " Invalid Expression!"
215
+ forever readEvalPrint
216
+
217
+ readEvalPrint = do putStr " Expression? "
218
+ s <- getLine
219
+ case parse expr s of
220
+ Just (e," " ) -> do putStr " Value: "
221
+ print (eval e 0 )
222
+ _ -> putStrLn " Syntax error!"
0 commit comments