My solutions to the 2022 CS141 Specimen Paper. Find the original pdf at https://warwick.ac.uk/fac/sci/dcs/teaching/material/cs141/cs1410-specimen.pdf. Parts of Question 3 and of Question 6 are left out.
This question is about the fundamentals ofprogramming in Haskell.
(a) For each term, first desugar it, then evaluate it as far as possible.Give all intermediate steps of the evaluation.
Alex covered this in the revision lecture, so I won't go over it again.
(b) Describe the usage of the (.) combinator in typical Haskell code. A definition of (.) is given in the Appendix
(.) is a right-associative infix operator of type(b -> c) -> (a -> b) -> (a -> c) which describes functioncomposition. We can compose two functions f and g as f.g, normallydenoted f(g(x)) in mathematics. Example usage is given below.
haskell
-- returns the second successorsucc2 :: Ord a => a -> a
succ2 = succ . succ
(c) (...) Define an equivalent function concatMap’ in terms of a single callto foldr with suitable arguments so that it is implicitly recursive.
haskell
concatMap' :: (a -> [b]) -> [a] -> [b]
concatMap' f = foldr (\x ys -> f x ++ ys) []
(d) Each of the following expressions is polymorphic. For each expression,give two distinct monomorphic typings. All functions used are listedin the Appendix.
haskell
-- (i) 5 :: Int5 :: Integer-- (ii)(\x y z -> y) :: () -> () -> () -> ()
(\x y z -> y) :: () -> Int -> () -> Int-- (iii)(minimum $ pure $ pure False) :: [Bool]
(minimum $ pure $ pure False) :: MaybeBool--- (iv)null :: [Int] -> Boolnull :: [Double] -> Bool
Question 2
This question is about recursive and higher-order functions.
(a) Define a function isPrime :: Integer -> Bool which given aninteger value, determines whether it is a prime number.
haskell
isPrime :: Integer -> BoolisPrime1 = FalseisPrime n = not $ any (== 0) [ x `mod` n | x <- [2..n] ]
(b) Using a list comprehension and isPrime, write a definitionof primes :: [Integer] which constructs the infinite listof prime numbers.
haskell
primes :: [Integer]
primes = [ x | x <- [2..], isPrime x ]
(c) (i) Using your answer to (b), write a functiontoPrime :: Char -> Integer which maps each character inthe alphabet to a unique prime number, ignoring capitalisation.
(c) (ii) With the help of toPrime, define a functionscore :: String -> Integer which calculates the product of allthe corresponding prime numbers for the characters in the input string.
(c) (iii) With the help of score, define a functionisAnagram :: String -> String -> Bool which determineswhether two String values are anagrams of each other.
(c) (iv) With the help of the previous definitions, define a functionanagrams :: [String] -> [(String, [String])] which, givena list of String values representing a list of words, returns a list ofpairs, of the same length, in which every input word is pairedwith a list of all its anagrams found in the input (except itself).
haskell
anagrams :: [String] -> [(String, [String])]
anagrams xs = map (\x -> (x, [ t | t <- (xs \\ [x]), isAnagram x t ])) cs
(c) (v) With the help of the previous definitions, define a functionsubgrams :: [String] -> [(String, [String])] which behaveslike anagrams, except it also includes words which do not useevery character from the original word.
haskell
subgrams :: [String] -> [(String, [String])]
subgrams xs = map (\x -> (x, [t | t <- (xs \\ [x]), isSubgram t x ])) xs
where isSubgram n m = score m `mod` score n == 0
Question 3
(b) The semantics of (Report) Haskell include three principal formsof polymorphism. Name the two forms which specifically relate totype classes.
Ad-Hoc Polymorphism and Subtype Polymorphism
(c) Declare a type class called Default, which describes, for anyimplementing type a, a value called def of type a.
Question 4
This question is about data structures.
(a) Use record syntax to give a suitable polymorphic definitionfor the Tape data type
haskell
dataTape a = Tape {
ls :: [a],
focus :: a,
rs :: [a]
}
(b) Define singleton :: a -> Tape a, which is a tape with a single value.
haskell
singleton :: a -> Tape a
singleton x = Tape [] x []
(c) Define moveLeft :: Tape a -> Tape a, which moves the focusof the tape one element to the left if (and only if) the left sublistis nonempty. If it is empty, the tape should remain unchanged.
haskell
moveLeft :: Tape a -> Tape a
moveLeft t@(Tape [] _ _) = t
moveLeft (Tape (x:xs) f rs) = Tape xs x (f:rs)
(d) Define a function fromList, which converts a list into a Tape if possible.You should choose an appropriate type for fromList; ensure thatyour function is total.
haskell
fromList :: [a] -> Tape a
fromList (x:xs) = Tape [] x xs
fromList [] = error "Empty list cannot be converted to a Tape"
(f) Tape can be folded. Define a suitable Foldable instance for Tape
haskell
instanceFoldableTapewhere-- foldr :: (a -> b -> b) -> b -> t a -> b foldr f z (Tape ls fc rs) = foldr f (f fc foldedLs) rs
where foldedLs = foldr f z ls
(g) Define a functionsplitAt :: (a -> Bool) -> Tape a -> ([a], [a]) (...)
haskell
splitAt :: (a -> Bool) -> Tape a -> ([a], [a])
splitAt f = (\t -> (reverse $ ls t, rs t)) . until (f . focus) moveRight . toLeft
Question 5
This question is about functors, applicative functors,and monads.
(a) The Functor, Applicative and Monad type classes form part ofthe type class hierarchy. What is the type class hierarchy? Describethe subtype relationships between these three type classes.
Monad is a subtype of Applicative, which is a subtype of Functor. Monad is older than Applicative, and it's return function is identical to Applicative's pure function beyond the typeclass constraints.
(b) State s is a functor. Give a suitable Functor instance for State s.
haskell
instanceFunctor (States) where-- fmap :: (a -> b) -> State s a -> State s b fmap f st = State $ \s -> let (x, y) = runState st $ s in (f x, y)
(c) State s is an applicative functor. Give a suitable Applicativeinstance for State s.
haskell
instanceApplicative (States) where-- (<*>) :: State s f -> State s x -> State s (f x) sf <*> sx = State $ \s ->
let (f, s1) = (runState sf) s
(x, s2) = (runState sx) s1
in (f x, s2)
(d) State s is a monad. Give a suitable Monad instance for State s.
haskell
instanceMonad (States) where-- (>>=) :: (State s a) -> (a -> State s b) -> (State s b) sa >>= f = State $ \s -> let (x, s1) = runState sa s
in runState (f x) s1
(d) Each of the following expressions is polymorphic. For each expression,give two distinct monomorphic typings. All functions used are listedin the Appendix.
haskell
-- (i) 5 :: Int5 :: Integer-- (ii)(\x y z -> y) :: () -> () -> () -> ()
(\x y z -> y) :: () -> Int -> () -> Int-- (iii)(minimum $ pure $ pure False) :: [Bool]
(minimum $ pure $ pure False) :: MaybeBool--- (iv)null :: [Int] -> Boolnull :: [Double] -> Bool
Question 6
This question is about writing complete functional programs
(c) Write a Haskell program which solves the classic FizzBuzzprogramming problem.
haskell
module Main wheremain :: IO ()
main = putStrLn . unlines $ map solve [1..]
solve :: Int -> Stringsolve n = if s == ""then show n else s where s = fizz n ++ buzz n
fizz, buzz :: Int -> Stringfizz n = if n `mod` 3 == 0then"Fizz"else""buzz n = if n `mod` 5 == 0then"Buzz"else""
(d) Define intercept :: (String -> String) -> IO () -> IO (),which takes a function f and an IO action a and uses takeOutputto apply the function f to each line of text generated by action,printing the modified version to the terminal’s standard output.
haskell
intercept :: (String -> String) -> IO () -> IO ()
intercept f c = takeOutput c >>= mapM_ (putStrLn . f) . lines