2020-10-01 17:46:28 -07:00
|
|
|
-- | Homework 2 template. See the homework description page for more details,
|
|
|
|
-- hints, and things to think about for each part.
|
|
|
|
module HW2 where
|
|
|
|
|
|
|
|
import HW1
|
|
|
|
|
|
|
|
-- Copied from my HW1 for your convenience!
|
2020-10-06 21:41:27 -07:00
|
|
|
fold' :: (Int -> a) -> (a -> a -> a) -> (a -> a -> a) -> Expr -> a
|
|
|
|
fold' f1 f2 f3 = rec
|
2020-10-01 17:46:28 -07:00
|
|
|
where
|
|
|
|
rec (Lit i) = f1 i
|
|
|
|
rec (Add l r) = f2 (rec l) (rec r)
|
|
|
|
rec (Mul l r) = f3 (rec l) (rec r)
|
|
|
|
|
|
|
|
--
|
|
|
|
-- * Part 1: Reverse Polish Notation
|
|
|
|
--
|
|
|
|
|
|
|
|
-- | Takes an expression and returns a string encoding of that expression in
|
|
|
|
-- Reverse Polish Notation (RPN).
|
|
|
|
--
|
|
|
|
-- >>> toRPN (Lit 3)
|
|
|
|
-- "3"
|
|
|
|
--
|
|
|
|
-- >>> toRPN e1
|
|
|
|
-- "2 3 4 * +"
|
|
|
|
--
|
|
|
|
-- >>> toRPN e2
|
|
|
|
-- "7 6 + 5 *"
|
|
|
|
--
|
|
|
|
-- >>> toRPN e3
|
|
|
|
-- "3 2 * 5 4 * +"
|
|
|
|
--
|
|
|
|
-- >>> elem (toRPN e4) ["8 7 9 * + 6 +", "8 7 9 * 6 + +"]
|
|
|
|
-- True
|
|
|
|
--
|
|
|
|
toRPN :: Expr -> String
|
2020-10-06 21:41:27 -07:00
|
|
|
toRPN = fold' show (rpn "+") (rpn "*")
|
2020-10-01 17:46:28 -07:00
|
|
|
where rpn op l r = concat [l, " ", r, " ", op]
|
|
|
|
|
|
|
|
|
|
|
|
-- | Takes a string that is an RPN-encoded expression and produces the same
|
|
|
|
-- expression represented as an abstract syntax tree.
|
|
|
|
--
|
|
|
|
-- You can assume that your function will only be given valid RPN-encodings
|
|
|
|
-- of expressions. That is, it need not fail gracefully if it encounters an
|
|
|
|
-- error. However, if you would like to improve the error handling, you are
|
|
|
|
-- welcome to change the type of your function and the doctests.
|
|
|
|
--
|
|
|
|
-- >>> fromRPN "3"
|
|
|
|
-- Lit 3
|
|
|
|
--
|
|
|
|
-- >>> fromRPN "2 3 +"
|
|
|
|
-- Add (Lit 2) (Lit 3)
|
|
|
|
--
|
|
|
|
-- >>> fromRPN "2 3 4 + +"
|
|
|
|
-- Add (Lit 2) (Add (Lit 3) (Lit 4))
|
|
|
|
--
|
|
|
|
-- >>> all (\e -> e == fromRPN (toRPN e)) [e1,e2,e3,e4]
|
|
|
|
-- True
|
|
|
|
--
|
|
|
|
fromRPN :: String -> Expr
|
|
|
|
fromRPN = head . foldl step [] . words
|
|
|
|
where
|
|
|
|
step (l:r:es) "+" = (Add r l):es
|
|
|
|
step (l:r:es) "*" = (Mul r l):es
|
|
|
|
step es s = (Lit (read s)):es
|
|
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
-- * Part 2: Syntactic Sugar
|
|
|
|
--
|
|
|
|
|
|
|
|
-- | Takes an expression and returns an expresion that evaluates to its
|
|
|
|
-- negation. Notice that this function does *not* evaluate the expression!
|
|
|
|
-- It returns a new expression that, when evaluated, will evaluate to the
|
|
|
|
-- negation of the original expression.
|
|
|
|
--
|
|
|
|
-- >>> eval e2
|
|
|
|
-- 65
|
|
|
|
--
|
|
|
|
-- >>> eval (neg e2)
|
|
|
|
-- -65
|
|
|
|
--
|
2020-10-06 21:41:27 -07:00
|
|
|
neg = fold' (Lit . negate) Add (Mul . neg) -- Not efficient, but short :^)
|
2020-10-01 17:46:28 -07:00
|
|
|
|
|
|
|
|
|
|
|
-- | Takes two expressions and returns an expression that evalautes to the
|
|
|
|
-- second expression subtracted from the first. Once again, note that the
|
|
|
|
-- return type is an expression.
|
|
|
|
--
|
|
|
|
-- >>> eval e1
|
|
|
|
-- 14
|
|
|
|
--
|
|
|
|
-- >>> eval (sub e2 e1)
|
|
|
|
-- 51
|
|
|
|
--
|
|
|
|
sub = fmap (. neg) Add -- (a -> b) is a Functor
|