Haskell: Evaluate any Ast into a string - haskell

I have an assigment where I have to program an evaluation function eval::Ast -> String which evaluates any Ast into a string. for example:
>eval (parse "hel + lo")
"hello"
> eval (parse "mi + 2 * la")
"milala"
I've made a parse-function that e.g. takes "hel + lo" and returns Plus (Word "hel") (Word "lo")
But I am very unsure of what the function (and types) should look like as there are multiple types involved in the evaluation of the AST... Anyone who can put me in the right direction?
The AST is defined as
data Ast
= Word String
| Num Int
| Mult Ast Ast
| Plus Ast Ast
| Minus Ast Ast
deriving (Eq, Show)

eval :: AST -> String is what defines the semantics of your operations. For example, eval (Plus (Word x) (Word y)) == x ++ y.
However, what should eval (Plus x y) produce if both arguments are numbers? Should it concatenate them, or add them numerically? What if either one itself is one of the Plus, Minus, or Mul values? You would need to evaluate them first, but then you can't tell if a result like "2" is really a string, or the result of a number.
You also need to decide if every AST can be evaluated: does Mul (Word "x") (Word "y") make sense? If not, what String would you return?
I would suggest two things:
First, define a simplify function that takes care of reducing the AST to something simpler. It will take care of doing things like numerical arithmetic in the case of Plus (Num ...) (Num ...), but leave things like Word x or Plus (Word x) (Num y) unchanged.
simplify :: AST -> AST
simplify (Num x) = Num x -- Easy case done for you; no simplification necessary
simplify (Word x) = ...
-- Hint: in each of the following, you need to recursively simplify the operands.
-- Once you do that, you can either simplify further, or return a new
-- node to be evaluated.
simplify (Plus x y) = ...
simplify (Minus x y) = ...
simplify (Mul x y) = ...
Second, define eval :: AST -> Either String String, will first use simplify on its argument, then do case-by-case conversions of ASTs to Strings where feasible, and returning an appropriate error message where it isn't.
eval :: AST -> Either String String
eval x = eval' (simplify x)
where eval' (Word x) = Right x -- Easy case done for you
eval' (Num x) = ...
-- simplify will have reduced all the Num + Num, Num - Num, and
-- Num * Num nodes to single Num nodes already.
eval' (Plus (Word x) (Word y)) = x ++ y -- Other easy case done above
eval' (Plus x y) = ...
eval' (Minus (Word x) (Word y)) = ...
eval' (Minus x y) = ...
eval' (Mul (Word x) (Word y)) = ...
eval' (Mul x y) = ...
Note that you could conceivably define some combinations in either eval' or simplify, e.g.
simplify (Plus (Word x) (Word y)) == Word $ x ++ y
leaving the more complicated (and possibly impossible) cases to eval.

Related

Adding two numbers together without using the + operator in Haskell

I want to add two positive numbers together without the use of any basic operators like + for addition. I've already worked my way around that (in the add''' function) (i think) may not be efficient but thats not the point right now. I am getting lots of type errors however which i have no idea how to handle, and is very confusing for me as it works on paper and i've come from python.
add 1245 7489
--add :: Int -> Int -> Int
add x y = add'' (zip (add' x) (add' y))
where
add' :: Int -> [Int]
add' 0 = []
add' x = add' (x `div` 10) ++ [x `mod` 10]
conversion [1,2,4,5] [7,4,8,9] then zipping them together [(1,7),(2,4)....]
add'' :: [(Int,Int)] -> [Int]
add'' (x:xs) = [(add''' (head x) (last x))] ++ add'' xs
summary [8,6,...] what happens when the sum reaches 10 is not implemented yet.
where
--add''' :: (Int,Int) -> Int
add''' x y = last (take (succ y) $ iterate succ x)
adding two numbers together
You can't use head and last on tuples. ...Frankly, you should never use these functions at all because they're unsafe (partial), but they can be used on lists. In Haskell, lists are something completely different from tuples.To get at the elements of a tuple, use pattern matching.
add'' ((x,y):xs) = [add''' x y] ++ add'' xs
(To get at the elements of a list, pattern matching is very often the best too.) Alternatively, you can use fst and snd, these do on 2-tuples what you apparently thought head and last would.
Be clear which functions are curried and which aren't. The way you write add''', its type signature is actually Int -> Int -> Int. That is equivalent to (Int, Int) -> Int, but it's still not the same to the type checker.
The result of add'' is [Int], but you're trying to use this as Int in the result of add. That can't work, you need to translate from digits to numbers again.
add'' doesn't handle the empty case. That's fixed easily enough, but better than doing this recursion at all is using standard combinators. In your case, this is only supposed to work element-wise anyway, so you can simply use map – or do that right in the zipping, with zipWith. Then you also don't need to unwrap any tuples at all, because it works with a curried function.
A clean version of your attempt:
add :: Int -> Int -> Int
add x y = fromDigits 0 $ zipWith addDigits (toDigits x []) (toDigits y [])
where
fromDigits :: Int -> [Int] -> Int
fromDigits acc [] = acc
fromDigits acc (d:ds)
= acc `seq` -- strict accumulator, to avoid thunking.
fromDigits (acc*10 + d) ds
toDigits :: Int -> [Int] -> [Int] -- yield difference-list,
toDigits 0 = id -- because we're consing
toDigits x = toDigits (x`div`10) . ((x`mod`10):) -- left-associatively.
addDigits :: Int -> Int -> Int
addDigits x y = last $ take (succ x) $ iterate succ y
Note that zipWith requires both numbers to have the same number of digits (as does zip).
Also, yes, I'm using + in fromDigits, making this whole thing pretty futile. In practice you would of course use binary, then it's just a bitwise-or and the multiplication is a left shift. What you actually don't need to do here is take special care with 10-overflow, but that's just because of the cheat of using + in fromDigits.
By head and last you meant fst and snd, but you don't need them at all, the components are right there:
add'' :: [(Int, Int)] -> [Int]
add'' (pair : pairs) = [(add''' pair)] ++ add'' pairs
where
add''' :: (Int, Int) -> Int
add''' (x, y) = last (take (succ y) $ iterate succ x)
= iterate succ x !! y
= [x ..] !! y -- nice idea for an exercise!
Now the big question that remains is what to do with those big scary 10-and-over numbers. Here's a thought: produce a digit and a carry with
= ([(d, 0) | d <- [x .. 9]] ++ [(d, 1) | d <- [0 ..]]) !! y
Can you take it from here? Hint: reverse order of digits is your friend!
the official answer my professor gave
works on positive and negative numbers too, but still requires the two numbers to be the same length
add 0 y = y
add x y
| x>0 = add (pred x) (succ y)
| otherwise = add (succ x) (pred y)
The other answers cover what's gone wrong in your approach. From a theoretical perspective, though, they each have some drawbacks: they either land you at [Int] and not Int, or they use (+) in the conversion back from [Int] to Int. What's more, they use mod and div as subroutines in defining addition -- which would be okay, but then to be theoretically sound you would want to make sure that you could define mod and div themselves without using addition as a subroutine!
Since you say efficiency is no concern, I propose using the usual definition of addition that mathematicians give, namely: 0 + y = y, and (x+1) + y = (x + y)+1. Here you should read +1 as a separate operation than addition, a more primitive one: the one that just increments a number. We spell it succ in Haskell (and its "inverse" is pred). With this theoretical definition in mind, the Haskell almost writes itself:
add :: Int -> Int -> Int
add 0 y = y
add x y = succ (add (pred x) y)
So: compared to other answers, we can take an Int and return an Int, and the only subroutines we use are ones that "feel" more primitive: succ, pred, and checking whether a number is zero or nonzero. (And we land at only three short lines of code... about a third as long as the shortest proposed alternative.) Of course the price we pay is very bad performance... try add (2^32) 0!
Like the other answers, this only works for positive numbers. When you are ready for handling negative numbers, we should chat again -- there's some fascinating mathematical tricks to pull.

Haskell pattern-matching idiom

I'm making a calculator on abstract integers and I'm doing an awful lot of pattern matching. I can write
add Zero x = x
add (P x) y = next $ add (prev $ P x) y
add (N x) y = prev $ add (next $ N x) y
or
add Zero x = x
add x y = case x of
P _ -> next $ add (prev x) y
_ -> prev $ add (next x) y
While the first way is shorter, something in the second way appeals to me more.
Which is the preferred way to do this?
Use as-patterns.
add Zero y = y
add x#(P _) y = next $ add (prev x) y
add x#(N _) y = prev $ add (next x) y
I'd also consider abstracting out the common structure of your two recursive branches by noting that you just swap the roles of the prev and next functions depending on whether x is positive or negative:
add Zero x = x
add x y = f $ add (g x) y
where (f, g) = case x of
P _ -> (next, prev)
N _ -> (prev, next)
About this style:
add Zero x = x
add x y = case x of
P _ -> next $ add (prev x) y
_ -> prev $ add (next x) y
On the positive side, it avoids some repetition, which is good.
On the negative side, the case looks to be non-exhaustive at a first sight. Indeed, to convince oneself that the pattern match is really exhaustive, we have to reason about the possible values for the x in case x of, and see that at runtime that can not be Zero, because that was handled above. This requires far more mental effort than the first snippet, which is obviously exhaustive.
Worse, when turning on warnings, as we should always do, GHC complains since it is not convinced that the case is exhaustive.
Personally, I wish the designers of Haskell had forbidden non exhaustive matches entirely. I'd use a -Werror-on-non-exhaustive-matches if there were one. I would like to be forced to write e.g.
case something of
A -> ...
B -> ...
_ -> error "the impossible happened"
than having the last branch being silently inserted by the compiler for me.
Consider using the math-style definition of integers as congruence classes of pairs of naturals under the equivalence relation:
{((a,b), (c,d)) | b+c == d+a}
The intuition is that the pair of naturals (a,b) represents b-a. As mentioned in the Wikipedia article, this often reduces the number of special cases compared to the "0/positive/negative" definition. In particular, the addition operation you ask about implementing becomes a one-liner:
-- both Int and Integer are taken
data Int' = Int Nat Nat
instance Num Int' where
-- b-a + d-c = (b+d)-(a+c)
Int a b + Int c d = Int (a + c) (b + d)
It's kind of fun to work through the different operations with this representation. For example, Eq can be implemented with the equation given above, and Ord is similar:
instance Eq Int' where
-- b-a == d-c = b+c == d+a
Int a b == Int c d = b+c == d+a
instance Ord Int' where
-- compare (b-a) (d-c) = compare (b+c) (d+a)
compare (Int a b) (Int c d) = compare (b+c) (d+a)
On occasion, it can be handy to normalize these things. Just like fractions can be reduced by multiplying the numerator and denominator by the same number until they're relatively prime, these things can be reduced by adding or subtracting the same number to both parts until (at least) one of them is zero.
normalize (Int (S a) (S b)) = normalize (Int a b)
normalize v = v

getting rid of unnecessary parenthesis

I wrote a function for evaluating a polynomial at a given number. The polynomial is represented as a list of coefficients (e.g. [1,2,3] corresponds to x^2+2x+3).
polyEval x p = sum (zipWith (*) (iterate (*x) 1) (reverse p))
As you can see, I first used a lot of parenthesis to group which expressions should be evaluated. For better readability I tried to eliminate as many parenthesis using . and $. (In my opinion more than two pairs of nested parenthesis are making the code more and more difficult to read.) I know that function application has highest priority and is left associative. The . and $are both right associative but . has priority 9, while $ has priority 0.
So it seemed to me that following expression cannot be written with even fewer parenthesis
polyEval x p = sum $ zipWith (*) (iterate (*x) 1) $ reverse p
I know that we need parenthesis for (*) and (*x) to convert them to prefix functions, but is it possible to somehow remove the parenthesis around iterate (*x) 1?
Also what version would you prefer for readability?
I know that there are many other ways to achieve the same, but I'd like to discuss my particular example, as it has a function evaluated in two arguments (iterate (*x) 1) as middle argument of another function that takes three arguments.
As usual with this sort of question I prefer the OP's version to any of the alternatives that have been proposed so far. I would write
polyEval x p = sum $ zipWith (*) (iterate (* x) 1) (reverse p)
and leave it at that. The two arguments of zipWith (*) play symmetric roles in the same way that the two arguments of * do, so eta-reducing is just obfuscation.
The value of $ is that it makes the outermost structure of the computation clear: the evaluation of a polynomial at a point is the sum of something. Eliminating parentheses should not be a goal in itself.
So it might be a little puerile, but I actually really like to think of Haskell’s rules in terms of food. I think of Haskell’s left-associative function application f x y = (f x) y as a sort of aggressive nom or greedy nom, in that the function f refuses to wait for the y to come around and immediately eats the f, unless you take the time to put these things in parentheses to make a sort of "argument sandwich" f (x y) (at which point the x, being uneaten, becomes hungry and eats the y.) The only boundaries are the operators and the special forms.
Then within the boundaries of the special forms, the operators consume whatever is around them; finally the special forms take their time to digest the expressions around them. This is the only reason that . and $ are able to save some parentheses.
Finally this we can see that iterate (* x) 1 is probably going to need to be in a sandwich because we don't want something to just eat iterate and stop. So there is no great way to do that without changing that code, unless we can somehow do away with the third argument to zipWith -- but that argument contains a p so that requires writing something to be more point-free.
So, one solution is to change your approach! It makes a little more sense to store a polynomial as a list of coefficients in the already-reversed direction, so that your x^2 + 2 * x + 3 example is stored as [3, 2, 1]. Then we don't need to perform this complicated reverse operation. It also makes the mathematics a little simpler as the product of two polynomials can be rewritten recursively as (a + x * P(x)) * (b + x * Q(x)) which gives the straightforward algorithm:
newtype Poly f = Poly [f] deriving (Eq, Show)
instance Num f => Num (Poly f) where
fromInteger n = Poly [fromInteger n]
negate (Poly ps) = Poly (map negate ps)
Poly f + Poly g = Poly $ summing f g where
summing [] g = g
summing f [] = f
summing (x:xs) (y:ys) = (x + y) : summing xs ys
Poly (x : xs) * Poly (y : ys) = prefix (x*y) (y_p + x_q) + r where
y_p = Poly $ map (y *) xs
x_q = Poly $ map (x *) ys
prefix n (Poly m) = Poly (n : m)
r = prefix 0 . prefix 0 $ Poly xs * Poly ys
Then your function
evaluatePoly :: Num f => Poly f -> f -> f
evaluatePoly (Poly p) x = eval p where
eval = (sum .) . zipWith (*) $ iterate (x *) 1
lacks parentheses around iterate because the eval is written in pointfree style, so $ can be used to consume the rest of the expression. As you can see it unfortunately leaves some new parentheses around (sum .) to do this, though, so it might not be totally worth your while. I find the latter less readable than, say,
evaluatePoly (Poly coeffs) x = sum $ zipWith (*) powersOfX coeffs where
powersOfX = iterate (x *) 1
I might even prefer to write the latter, if performance on high powers is not super-critical, as powersOfX = [x^n | n <- [0..]] or powersOfX = map (x^) [0..], but I think iterate is not too hard to understand in general.
Perhaps breaking it down to more elementary functions will simplify further. First define a dot product function to multiply two arrays (inner product).
dot x y = sum $ zipWith (*) x y
and change the order of terms in polyEval to minimize the parenthesis
polyEval x p = dot (reverse p) $ iterate (* x) 1
reduced to 3 pairs of parenthesis.

Haskell - replace strings in list with values from a map

i have this problem:
i want to have a list of strings representing math expression, and a map of arguments to replace the variables.
so if my list is like ["x","+","y","-","5"] and arguments are [("x","5"),("y","4")]
the function should return ["5","+","4","-","5"]
i have this function to find key from map (from the Learn you a haskell book)
findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
findKey key [] = Nothing
findKey key ((k,v): xs) =
if key == k
then Just v
else findKey key xs
and then my function to replace the variables with values
takeValuesFromMap (x:str) m result
|x == [] = result
|findKey x m == Nothing = takeValuesFromMap str m (result++[x])
|otherwise = takeValuesFromMap str m result++[fromJust (findKey x m)]
if no match in the map, we pass the regular string. Otherwise we pass to the result the value staying next to the key that matches.
but in the end when i call
takeValuesFromMap ["x","+","y","-","5"] (Map.fromList [("x","5"),("y","4")]) []
it says
Solver.hs:63:48:
Couldn't match expected type `[([Char], [Char])]'
with actual type `Map.Map [Char] [Char]'
In the return type of a call of `Map.fromList'
In the second argument of `takeValuesFromMap', namely
`(Map.fromList [("x", "5"), ("y", "4")])'
In the expression:
takeValuesFromMap
["x", "+", "y", "-", ....]
(Map.fromList [("x", "5"), ("y", "4")])
[]
any idea how to fix this?
I'm going to take a different track here, and suggest that you don't solve this problem. The reason is that the list ["x","+","y","-","5"] is a very poor representation of the algebraic expression x + y - 5. I don't know exactly what you're trying to do, but a better approach would represent the expression as an abstract syntax tree, using an algebraic datatype.
So for example, we could use the following type to represent the expressions:
data Expr a = Variable String
| Literal a
| Plus Expr Expr
| Minus Expr Expr
Given this type, your example goes like this:
example :: Expr Integer
example = Minus (Plus (Var "x") (Var "y")) (Literal 5)
It's easy to write a function that evaluates expressions of this type, given a Map from variable names to values:
-- | Evaluate an expression, reading variable's values from the given environment
-- (the Map argument). Returns Nothing if any of the variables is undefined.
eval :: Num a => Expr a -> Map String a -> Maybe a
eval (Variable v) env = Map.lookup v env
eval (Literal x) _ = Just x
eval (Plus x y) env =
-- If this is confusing, read up on the Maybe monad in Learn You a Haskell
do x' <- eval x env
y' <- eval y env
return (x + y)
eval (Minus x y) env =
do x' <- eval x env
y' <- eval y env
return (x - y)
More complex, but well worth learning, is then to write a parser that takes a string and turns it into an Expr. If you're reading Learn You A Haskell, you may want to first get more comfortable with monads and applicatives. But when you're ready to take that step, there's a number of pages on the web with calculator parser examples:
http://meta-meta.blogspot.com/2007/10/simple-infix-calculator-in-haskell.html
http://www.youtube.com/playlist?list=PL_xuff3BkASMOzBr0hKVKLuSnU4UIinKx
http://haskelladdict.wordpress.com/2009/02/01/a-nice-little-calculator-implemented-in-haskell-and-parsec/
Though you may want to read this part of Real World Haskell first:
http://book.realworldhaskell.org/read/using-parsec.html
In findKey you require an association list, but you are actually using a Map. So, one way to fix it woulde be to remove Map.fromList.
Another point: Never replace pattern matching with equality checks! So please write:
| [] <- x = ...
| Nothing <- findKey x m = ...
Inspect the type of Map.fromList. Am I right in assuming that you take Map from Data.Map? If so:
:t Data.Map.fromList
Data.Map.fromList :: Ord k => [(k, a)] -> Map k a
So, this function returns Map, but your findKey actuall wants a list of tuples [([Char],[Char])]. You have got two choices now:
Use a function from Data.Map instead of your findKey to lookup a key.
Use another function to build your list.

What does eta reduce mean in the context of HLint

I'm looking at the tutorial http://haskell.org/haskellwiki/How_to_write_a_Haskell_program
import System.Environment
main :: IO ()
main = getArgs >>= print . haqify . head
haqify s = "Haq! " ++ s
When running this program under HLint it gives the following error;
./Haq.hs:11:1: Warning: Eta reduce
Found:
haqify s = "Haq! " ++ s
Why not:
haqify = ("Haq! " ++ )
Can someone shed some light on what exactly "Eta Reduce" means in this context?
Eta reduction is turning \x -> f x into f as long as f doesn't have a free occurence of x.
To check that they're the same, apply them to some value y:
(\x -> f x) y === f' y -- (where f' is obtained from f by substituting all x's by y)
=== f y -- since f has no free occurrences of x
Your definition of haqify is seen as \s -> "Haq! " ++ s, which is syntactic sugar for \s -> (++) "Haq! " s. That, in turn can be eta-reduced to (++) "Haq! ", or equivalently, using section notation for operators, ("Haq! " ++).
Well, eta reduction is (one way) to make point-free functions, and usually means that you can remove the last parameter of a function if it appears at the end on both sides of an expression.
f :: Int -> Int
g :: Int -> Int -> Int
f s = g 3 s
can be converted to
f = g 3
However, in this case it is slightly more complicated, since there is the syntactic sugar of two-parameter operator (++) on the rhs, which is type [a] -> [a] -> [a]. However, you can convert this to a more standard function:
haqify :: [Char] -> [Char]
haqify = (++) "Haq! "
Because (++) is an operator, there are other possibilities:
haqify = ("Haq! " ++ )
That is, the parens convert this into a one-parameter function which applies "Haq!" ++ to its argument.
From lambda calculus, we define eta conversion as the equality:
\x -> M x == M -- if x is not free in M.
See Barendregt, H. P. The Lambda Calculus: Its Syntax and Semantics, 1984.
In the Haskell context, see the definition on the Haskell wiki,
n eta conversion (also written η-conversion) is adding or dropping of abstraction over a function. For example, the following two values are equivalent under η-conversion:
\x -> abs x
and
abs
Converting from the first to the second would constitute an eta reduction, and moving from the second to the first would be an eta abstraction. The term 'eta conversion' can refer to the process in either direction.
Extensive use of η-reduction can lead to Pointfree programming. It is also typically used in certain compile-time optimisations.

Resources