Homework/HW1.md

3.2 KiB

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):

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 (+) (*)