[Haskell-beginners] Indenting
Brent Yorgey
byorgey at seas.upenn.edu
Sat Dec 5 15:09:01 EST 2009
On Sat, Dec 05, 2009 at 05:58:25PM +0000, John Moore wrote:
> Hi Brent,
> Sorry about the confusion below is the whole program, which may help or not?
> What i want to do is when the program prints out the answers , I would like
> it to be able to automatically by use of a function indent the answers
> depending how far down the answer goes.
OK, I think I understand what you want to do, although I'm not exactly
clear on how you want the indentation to work.
Anyway, I've interspersed my comments in the code below.
> import Maybe
Just FYI, "Maybe" is the old Haskell-98 name for the module, but
nowadays the standard name is "Data.Maybe".
> data Expression = Val Float
> | Add Expression Expression
> | Subtract Expression Expression
> | Multiply Expression Expression
> | Divide Expression Expression
> | Let String Expression Expression
> | Var String
> deriving Show
> type Dict =[(String,Expression)]
> emptyDict :: Dict
> emptyDict = []
> addEntry :: String->Expression ->Dict -> Dict
> addEntry n e d = (n,e): d
> lookupEntry :: String -> Dict -> Maybe Expression
> lookupEntry n [] = Nothing
> lookupEntry n (x:xs) = if (n == k)
> then (Just v)
> else lookupEntry n xs
> where (k,v) = x
I would write lookupEntry as follows:
lookupEntry _ [] = Nothing
lookupEntry n ((k,v):xs) | n == k = Just v
| otherwise = lookupEntry n xs
Note that you can do a nested pattern match ((k,v):xs), and the use of
guards instead of if...then...else.
But actually, lookupEntry is already in the standard Prelude, it is
called 'lookup'! So no need to reimplement it yourself.
> evalIO :: Dict -> Expression -> IO Float
This function is rather poor Haskell style, because it mixes up two
separate things: evaluating the expression, and printing the
result. Instead, I would do something like this:
eval :: Dict -> Expression -> ([String], Double)
where the output is the result paired with a "trace". (Note I have
used Double, a double-precision floating point number, instead of
Float, which is single-precision; there's usually very little reason
to use Float instead of Double.) Given eval, you can recover evalIO as follows:
evalIO :: Dict -> Expression -> IO Double
evalIO d e = do
let (trace, result) = eval d e
mapM_ putStrLn trace
return result
But this is a lot nicer because it cleanly separates the evaluation
from the IO, and you can now do anything you like with the trace ---
print it to a file, process it further, etc; you are not tied down to
printing it on the screen.
Now, if you just want the indentation to increase at each step, like so
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
...
then you can also separate this out: just return a trace with no
indentation, and then apply a function to the trace to indent
successive lines, something like
zipWith (++) (map (flip replicate ' ') [0..]) trace
If, on the other hand, you want the indentation to correspond to how
"deep" within the expression the evaluation is taking place, then you
can recursively pass along an extra parameter to your evaluation
function, like so:
eval :: Dict -> Expression -> ([String], Double)
eval d e = evalIndented 0 d e where
evalIndented i d (Val x) = ([], x)
evalIndented i d (Add x y) =
let (tx, vx) = evalIndented (i+1) d x
(ty, vy) = evalIndented (i+1) d y
in (replicate i ' ' ++ "Add " ++ show vx ++ " and " ++ show vy, vx + vy)
...and so on.
I hope this is helpful!
-Brent
More information about the Beginners
mailing list