import Test.QuickCheck {- Exam questions 2017-10 / 2017-12 -} -- Q1 2017-10 ------------------------- q1 :: [Int] -> Int q1 [] = 0 q1 [x] = x q1 (x:_:xs) = max x (q1 xs) -- What does this give: this = q1 (map abs [-1,-6,-5,7]) -- Q1 2017-12 ------------------------- q1' :: [Int] -> Int q1' [] = 0 q1' [x] = 0 q1' (x:y:xs) = max (q1' xs) y -- Write out the computation steps for computing the value of a1' = q1' [1,3,4,2] sol = [ q1' [1,3,4,2] , max (q1' [4,2]) 3 , max (max (q1' []) 2) 3 , max (max 0 2) 3 , 3] -- Note: this requires approximately 5 steps, so you should write five expressions, each one equivalent to q1' [1,3,4,2] -- and the last one should be a number (the final value of that expression). --------------------------------------- -- Q2 2017-10 ------------------------- -- Assume you have: type WeekNumber = Int rainfall :: WeekNumber -> Double -- assume this function exists rainfall = undefined -- A week is dry when rainfall < 5 -- Complete the definition of the following function: dryWeeks :: WeekNumber -> Int dryWeeks n | n < 1 = 0 | otherwise = countDry n + dryWeeks (n-1) countDry n | rainfall n < 5 = 1 | otherwise = 0 -- such that dryWeeks n (when n > 0) gives the number of dry weeks -- in the range 1 up to n. -- Your solution MUST be recursive -- Q2 2017-12 ------------------------- -- A week is considered to be “wet” if the -- rainfall in that week is more than 10 -- Give a definition of a function countWetWeeks :: WeekNumber -> Int countWetWeeks n = length [w | w <- [1..n], rainfall w > 10 ] -- such that countWetWeeks n (when n > 0) give the total count of the number of wet weeks in the week range 1 up to n. -- Your solution must use a list comprehension, but may also use other predefined functions. RECURSION = FAIL --------------------------------------- -- Q3 2017-10 ------------------------- {- In this question you should define a data type to represent a bus ticket of a certain kind described below. A bus ticket is either (i) a single ticket (a ticket valid for a certain number of minutes) or a (ii) period ticket (a ticket that lasts a number of whole days). A single ticket is marked with the date and time when it expires. A period ticket is marked with the date when it expires. You should use the types Date and Time given below (although the details of their definitions are not important for this question): -} type Year = Int type Month = Int type Day = Int type Hour = Int type Minute = Int data Date = Date Year Month Day data Time = Time Hour Minute -- Your task is (only) to complete the following definition: data BusTicket = Single Date Time | Period Date -- optional: add an Int to each of these for the duration -- Q3 2017-12 ------------------------- {- In this question you should define a data type to represent a picture made of simple geometric shapes. More precisely, a picture is a list of zero or more simple shapes. A simple shape is either a circle or a rectangle, and has a colour (either black, red, green, or blue), and a position on the coordinate plane. If it is a circle it has a specified diameter (an integer of unspecified units). If it is a rectangle it has a height and a width (also integers). Your task is (only) to complete the following Haskell definition of a data type Picture to represent this: -} data Picture = Picture [Shape] data Shape = Circle Colour Position Integer | Rectangle Colour Position Integer Integer -- together with any other types that you need for this purpose. You may make use the following definitions: data Colour = Black | Red | Green | Blue type Position = (Int,Int) --------------------------------------- -- Q4 2017-10 ------------------------- data Expr = X | Num Int | BinOp Op Expr Expr deriving (Eq,Show) data Op = Add | Mul | Subtract deriving (Eq,Show) -- Subtraction is represented but not really needed: -- 100 - X can be written as 100 + (-1) * X. ex4 = BinOp Subtract (Num 100) X ex4' = BinOp Add (Num 100) (BinOp Mul (Num (-1)) X) prop_removeSub = removeSub ex4 == ex4' -- Define removeSub :: Expr -> Expr removeSub = undefined -- Q4 2017-12 ------------------------- -- (Given a similar datatype but without variables) -- Define a function -- data Expr = Num Int | BinOp Op Expr Expr largestNum :: Expr -> Int -- which given an expression, returns the largest number that appears in the expression. largestNum (Num n) = n largestNum (BinOp op e1 e2) = largestNum e1 `max` largestNum e2 --------------------------------------- -- Q5 2017-10 ------------------------- -- Define a quickCheck property prop_take :: Int -> String -> Bool {- which relates the function isPrefixOf with the function take :: Int -> [a] -> [a] Your definition must use the two arguments as part of the test. -} prop_take n s = take n s `isPrefixOf` s -- Q5 2017-12 ------------------------- check :: Int -> Bool check n | (n >= 10) == True && (n <= 40) == True = True | (n >= 50) == True && (n <= 104) == True = True | otherwise = False check' n = (n >= 10) && (n <= 40) || (n >= 50) && (n <= 104) {- Rewrite this definition so that it does not use the equality operator (==) and also does not use definition by cases (|), and does not use if-then -else. -} --------------------------------------- -- Q6 2017-10 ------------------------- data Suit = Hearts | Clubs | Diamonds | Spades deriving (Eq,Show) data Rank = Numeric Int | Jack | Queen | King | Ace deriving (Eq,Show) data Card = Card Rank Suit deriving (Eq,Show) isRed, isDiamond :: Suit -> Bool isRed s = s == Hearts || s == Diamonds isDiamond s = s == Diamonds isAce, isLow :: Rank -> Bool isAce r = r == Ace isLow (Numeric n) = n < 5 isLow _ = False lowDiamonds cs = [Card r s | Card r s <- cs, isLow r && isDiamond s ] redAces cs = [Card r s | Card r s <- cs, isAce r && isRed s ] lowRedCards cs = [Card r s | Card r s <- cs, isLow r && isRed s ] {- The last three functions in this code contain a lot of “cut-and-paste” repetition. Define a function which generalises these three functions: -} selectCards :: (Rank -> Bool) -> (Suit -> Bool) -> [Card] -> [Card] selectCards rankP suitP cs = [Card r s | Card r s <- cs, rankP r && suitP s ] -- so that: prop_selectCards cs = lowDiamonds cs == selectCards isLow isDiamond cs && redAces cs == selectCards isAce isRed cs && lowRedCards cs == selectCards isLow isRed cs -- Q6 2017-12 ------------------------- -- Give a recursive definition of the following standard function: filter :: (a -> Bool) -> [a] -> [a] filter p xs = [ x | x <- xs, p x ] filter' p [] = [] filter' p (x:xs) | p x = x: filter' p xs | otherwise = filter' p xs --------------------------------------- -- Q7 2017-10 ------------------------- -- Give the definition of a QuickCheck generator quadlist :: Gen [Integer] {- for lists of Integers, where for every list generated, the length of the list is a multiple of 4. I.e., the generated lists contain 0 numbers, or 4 numbers, or 8 numbers, or 12 numbers, and so on. Hint: QuickCheck function vectorOf :: Int -> Gen a -> Gen [a] which generates a list of a specific length, as well as the generator arbitrary may be useful. -} quadlist = do n <- arbitrary vectorOf (4 * abs n) arbitrary -- Hints: (i) don’t make the common mistake of trying to apply a function of type Integer -> a to something of type Gen Integer, -- (ii) don’t forget that you can work with things of type Gen Integer using do-notation. -- Q7 2017-12 ------------------------- -- Give the definition of a QuickCheck generator lenlist :: Gen [Int] {- for lists of numbers, where for every list generated, the values of the elements in the list are not negative, but never larger than the length of the list. So for example, if it generates a list of ten elements, then every number in the list is no bigger that ten and no smaller than zero. -} lenlist = do n <- arbitrary vectorOf n $ choose (0,n)