#1. Note that the example down the page gives bold hints. The solution is:
doGetRandom :: Random a => RandomState a doGetRandom = do gen <- get let (val, gen') = random gen put gen' return val
Answers to the excercises from Real World Haskell, by O'Sullivan, Goerzen, and Stewart.
#1. Note that the example down the page gives bold hints. The solution is:
doGetRandom :: Random a => RandomState a doGetRandom = do gen <- get let (val, gen') = random gen put gen' return val
#1. The decision about dropping the parentheses depends on which side of the operator we're on. So, here's the new prettyShow -- changes in bold:
prettyShow :: (Show a, Num a) => SymbolicManip a -> String
-- Show a number or symbol as a bare number or serial
prettyShow (Number x) = show x
prettyShow (Symbol x) = x
prettyShow (BinaryArith op a b) =
let pa = maybeLeftParen a op
pb = maybeRightParen b op
pop = op2str op
in pa ++ pop ++ pb
prettyShow (UnaryArith opstr a) =
opstr ++ "(" ++ show a ++ ")"
Now, for the helper functions. Let's start with handling the left-paren:
maybeLeftParen :: (Show a, Num a) => SymbolicManip a -> Op -> String
maybeLeftParen x@(BinaryArith op1 _ _) op
| op1 == op && isRightAssociative op = "(" ++ prettyShow x ++ ")"
| (opPrio op1) < (opPrio op) = "(" ++ prettyShow x ++ ")"
| otherwise = prettyShow x
maybeLeftParen x _ = simpleParen x
For this to work, we need to know each operator's priority, and whether the operator is right-associative. These are trivial (the priorities are taken from Haskell's definitions; type :i (+) in ghci, for example):
opPrio :: Op -> Int opPrio Plus = 6 opPrio Minus = 6 opPrio Mul = 7 opPrio Div = 7 opPrio Pow = 8 isRightAssociative :: Op -> Bool isRightAssociatove Pow = True isRightAssociative _ = False
Finally, for handling parenthesis to the right of the operator, we have this:
maybeRightParen :: (Show a, Num a) => SymbolicManip a -> Op -> String
maybeRightParen x@(BinaryArith op1 _ _) op
| prio1 < prio = "(" ++ prettyShow x ++ ")"
| prio1 == prio && opIsMinusOrDiv = "(" ++ prettyShow x ++ ")"
| otherwise = prettyShow x
where prio1 = opPrio op1
prio = opPrio op
opIsMinusOrDiv = (op == Minus) || (op == Div)
maybeRightParen x _ = simpleParen x
#1. Trivially,
getElem4 :: (t,t,t,t) -> Int -> t getElem4 (a,_,_,_) 0 = a getElem4 (_,b,_,_) 1 = b getElem4 (_,_,c,_) 2 = c getElem4 (_,_,_,d) 3 = d
#2. Not too different:
getElem6 :: (t,t,t,t,t,t) -> Int -> t getElem6 (a,_,_,_,_,_) 0 = a getElem6 (_,b,_,_,_,_) 1 = b getElem6 (_,_,c,_,_,_) 2 = c getElem6 (_,_,_,d,_,_) 3 = d getElem6 (_,_,_,_,e,_) 4 = e getElem6 (_,_,_,_,_,f) 5 = f
#3. There are two ways to go about this. First, we can implement getElem6 in terms of getElem4, like this:
getElem6' :: (t,t,t,t,t,t) -> Int -> t getElem6' (a,b,c,d,e,f) i | i < 4 = getElem4 (a,b,c,d) i | otherwise = getElem4 (c,d,e,f) (i - 2)
Note how the error cases are handled -- whenever we hand of an invalid index, it is getElem4 that will encounter and report the problem.
The second approach is to implement getElem4 in terms of getElem6. But how do we build a six-tuple when all we're given is a four-tuple? We could, for example, copy the 1st element twice (at the end), i.e., given (a,b,c,d), pass the constructed value (a,b,c,d,a,a) to getElem6. While this ensures the synthesized elements are of the correct type, we'd get the wrong result for indexes 4 and 5, unless we add our own error-checking.
A better alternative lies in directly indicating these values don't really exist, and any attempt to access them is an error:
getElem4' :: (t,t,t,t) -> Int -> t
getElem4' (a,b,c,d) i = getElem6 (a,b,c,d,er,er) i
where er = error "Out of range"