84 lines
3.2 KiB
Markdown
84 lines
3.2 KiB
Markdown
**Andrew\'s solution**:
|
||
|
||
(Hi Andrew! Long time since databases)
|
||
|
||
This solution is straightforward and simple, and is similar to
|
||
Nicholas\'. Just like all the other solutions, it pattern matches on the
|
||
arguments (instead of using \"case\" expressions), and uses the Prelude
|
||
functions. Everything\'s idiomatic, but I think that the last function
|
||
(\"pretty\") can use an improvement. Here\'s the code in question:
|
||
|
||
pretty (Mul l r) = a ++ " * " ++ b
|
||
where
|
||
a = case l of
|
||
(Add _ _) -> parens True (pretty l)
|
||
_ -> parens False (pretty l)
|
||
b = case r of
|
||
(Add _ _) -> parens True (pretty r)
|
||
_ -> parens False (pretty r)
|
||
|
||
Here, the code for \"a\" and \"b\" is almost identical, and uses an
|
||
unneeded auxiliary function. For one thing, you can merge the two
|
||
variables \"a\" and \"b\" into a function:
|
||
|
||
pretty (Mul l r) = wrap l ++ " * " ++ wrap r
|
||
where
|
||
wrap e = case e of
|
||
(Add _ _) -> parens True (pretty e)
|
||
_ -> parens False (pretty e)
|
||
|
||
Then, you can fuse the definition of \"parens\" with the definition of
|
||
\"wrap\" (passing booleans to functions is actually a code smell, see
|
||
[the boolean identity
|
||
crisis](https://programming-elm.com/blog/2019-05-20-solving-the-boolean-identity-crisis-part-1/)):
|
||
|
||
pretty (Mul l r) = wrap l ++ " * " ++ wrap r
|
||
where
|
||
parens e@(Add _ _) = "(" ++ pretty e ++ ")"
|
||
parens e = pretty e
|
||
|
||
**Nicholas\' solution**
|
||
|
||
This is another straightforward and idiomatic solution. The \"pretty\"
|
||
function is conservative, unlike Andrew\'s, but requires little code.
|
||
|
||
**Shu-Kan\'s solution**
|
||
|
||
The parenthesis placement is a little off in this one, and it doesn\'t
|
||
use built-in functions like \'max\', which could have been used in
|
||
maxLit. It also seems to import data.Typeable, which I don\'t really see
|
||
used here. To improve it, take a look at these lines:
|
||
|
||
maxLit (Add(x)(y)) = if maxLit(x) > maxLit(y) then maxLit(x) else maxLit(y)
|
||
maxLit (Mul(x)(y)) = if maxLit(x) > maxLit(y) then maxLit(x) else maxLit(y)
|
||
|
||
In Haskell, function application is written with a space: \"f x\"
|
||
instead of \"f(x)\". Writing \"maxLit(y)\" is equivalent to writing
|
||
\"maxLit (y)\". But then, you don\'t even need parentheses!
|
||
|
||
maxLit (Add x y) = if maxLit x > maxLit y then maxLit x else maxLit y
|
||
maxLit (Mul x y) = if maxLit x > maxLit y then maxLit x else maxLit y
|
||
|
||
Finally, the \"if x \> y then x else y\" idiom is just the \"max\"
|
||
function in Haskell\'s Prelude:
|
||
|
||
maxLit (Add x y) = max (maxLit x) (maxLit y)
|
||
maxLit (Mul x y) = max (maxLit x) (maxLit y)
|
||
|
||
**My solution**
|
||
|
||
I went a weird route. For one, I created a Num instance for Expr, so
|
||
that I could write expressions as \"3\*2\" instead of \"Mul (Lit 3) (Lit
|
||
2)). This definition is incomplete; it\'s missing the abs, negate, and
|
||
signum functions. However, it does make for funny definitions, and
|
||
handles precedence for me!
|
||
|
||
In addition, I wrote a \"fold\" function for the Expr data type, and
|
||
wrote all functions except for \"pretty\" using it. They ended up pretty
|
||
short:
|
||
|
||
leftLit = fold id const const
|
||
rightLit = fold id (flip const) (flip const)
|
||
maxLit = fold id max max
|
||
eval = fold id (+) (*)
|