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)