-- Lecture 7A -- Exam questions from 2011-12 import Data.List import Test.QuickCheck import System.Random import Data.Maybe import System.Directory -- 1 -- Give the type of the following function: q1 [] _ = [] q1 ((x:xs):xss) y = (x < y, True) : q1 xss y q1 :: Ord a => [[a]] -> a -> [(Bool,Bool)] q1' xss y = map (\(x:_) -> (x < y, True)) xss -- Redefine q1 without using recursion -- (you may use any recursive functions defined in the Prelude). -- (iii) -- Simplify the following function definition as much as possible -- and give its type: q1c x y | x /= False = odd y : [ ] | x == True && even y = [False] | otherwise = [] ++ [False] q1c' :: Integral n => Bool -> n -> [Bool] q1c' x y = [x && odd y] -- (iv) 4p -- Define a function maxDiff which, given a -- list of Integers, returns the largest difference between -- any two consecutive integers in the list. For example, -- maxDiff [3,1,-3,0,1,3,5] -- should return 4. -- Your definition must use a single tail-recursive -- helper function, and no other recursive functions. maxDiff = m 0 where m a (x:y:xs) = m (max a (abs $ x - y)) (y:xs) m a _ = a -- Question 2 -- (Picture) data Diagram = Done | Action String Diagram | Q String Diagram Diagram deriving Show example = Q "Raining?" umbrella (Action "Temp" warm) where umbrella = Action "Umbrella" (Action "Outside" Done) warm = Q "Warm?" sunny (Action "StayIn" Done) sunny = Q "Sunny?" (Action "Swim" Done) (Action "Park" Done) -- (ii) Define the following function: actions :: Diagram -> [Bool] -> [String] actions (Action s d) bs = s:actions d bs actions (Q _ yes no) (b:bs) = actions (if b then yes else no) bs actions _ _ = [] {- This function, given an action diagram, and a list of the answers to all questions on the way (False=no, True=yes), produces a list of actions that we need to take. You may assume that there are enough answers to the questions in the list, and that unused answers are ignored. Main> actions example [True] ["Grab your umbrella", "Go outside"] Main> actions example [False,True,True] ["Check the temperature", "Go swimming"] -} -- Question 3 data C4 = C4 [Column] deriving (Eq,Show) type Column = [Player] data Player = Red | Black deriving (Eq,Show) example1 = C4 [[],[],[Red],[Black,Black,Red],[Black],[],[]] example2 = C4 [[],[],[Red],[Black,Black,Red],[Black,Red],[],[]] -- (ii) {- The datatype invariant for a C4 is that (i) no column has more than 6 tokens, (ii) there are at most 7 columns, (iii) the difference between the number of tokens that each player has played is at most one. We will call a C4 with these properties a legal C4. Give a definition of a function -} legalC4 :: C4 -> Bool legalC4 (C4 cs) = all ((<= 6) . length) cs && length cs <= 7 && (count Red - count Black) `elem` [-1..1] where count p = length [ q | c <- cs, q <- c, q == p] -- = length $ filter (== p) $ concat cs -- which checks whether the given C4 is legal. -- (iii) Define a function play :: Player -> Int -> C4 -> Maybe C4 -- play Red 5 example1 == Just example2 -- play Black 1 example1 == Nothing play p i (C4 cs) | i > 0 && i <= length cs && legalC4 (C4 cs') = Just $ C4 cs' | otherwise = Nothing where (before,ci:after) = splitAt (i-1) cs cs' = before ++ [ci ++ [p]] ++ after -- (iv) run :: C4 -> Player -> [Int] -> C4 -- skip illegal moves run c4 p [] = c4 run c4 p (i:is) = case play p i c4 of Just c4' -> run c4' (other p) is Nothing -> run c4 p is other Red = Black other _ = Red --(v) {- Define a QuickCheck generator for legal C4s, and make C4 an instance of class Arbitrary. Hints: use the function run. QuickCheck function vectorOf :: Int -> Gen a -> Gen [a] may be useful here. -} instance Arbitrary C4 where arbitrary = do firstPlayer <- elements [Red,Black] turns <- choose (0,6*7) is <- vectorOf turns (choose(1,7)) return $ run emptyC4 firstPlayer is where emptyC4 = C4 $ replicate 7 [] -- 4 -- lookup :: Eq a => [(a,b)] -> a -> Maybe b -- data Either a b = Left a | Right b deriving (Eq, Show) {- The idea is to use the type Either String a to model a computation of a which might fail, where the string is a suitable error message. For convenience we define: -} type MayErr a = Either String a safeDiv :: Integral a => a -> a -> MayErr a safeDiv i j | j /= 0 = Right (i `div` j) | otherwise = Left ("Error: divide by zero") -- (i) 2p define failsWith :: Maybe a -> String -> MayErr a failsWith (Just a) _ = Right a failsWith Nothing s = Left s -- (ii) -- We can make MayErr an instance of Monad as follows {- instance Monad MayErr where Right v >>= f = f v Left s >>= _ = Left s return = Right -} {- -- Rewrite suspiciousCar :: LicenceNr -> Maybe (Name, Pid, Crime) -- to suspiciousCar' :: LicenceNr -> MayErr (Name, Pid, Crime) -- using do notation and `failswith` to -- give better error messages -} type LicenceNr = Int type Pid = Int type Crime = String type Name = String carRegister = [(123,456)] nameRegister = [(456,"bob")] crimeRegister = [("bob","bobbery")] -- Worse case: the three lookups are dependent on each other: suspiciousCar car = case lookup car carRegister of Nothing -> Nothing Just pnr -> case lookup pnr nameRegister of Nothing -> Nothing Just name -> case lookup name crimeRegister of Nothing -> Nothing Just crime -> Just (name,pnr,crime) -- which could be rewritten as: suspiciousCar' car = do prn <- lookup car carRegister `failsWith` ("Car "++ show car ++ "not found") name <- lookup pnr nameRegister `failsWith` "..." crime <- lookup name crimeRegister `failsWith` "..." return (name,pnr,crime)