Analysis of Past Papers

Recurring Themes / Topics

FP Replica Past Paper 1

Question 1: Types and Functions

(a) Give the type of f1, explain what the type means, and give the value of test_f1, where the definitions are:

f1 :: (Num a, Eq a) => a -> [a] -> [a]
f1 k xs = [x+k | x <- xs, x /= k]
test_f1 = f1 3 [2, 3, 4, -1, 5, 3]

(b) Give the type of f2 and the value of test_f2:

f2 f g h x = f (g (h x))
test_f2 = f2 (*2) (+3) (^2) 4

f2 :: (c -> d) -> (b -> c) -> (a -> b) -> a -> d

Value of test_f2: 2 * (3 + (4^2)) = 2 * (3 + 16) = 2 * 19 = 38

Question 2: Monads and Algebraic Data Types

(e) Rewrite the following code using do notation:

validateAndProcess :: Int -> Maybe String
validateAndProcess x =
   checkPositive x >>= \y ->
   checkEven y >>= \z ->
   return (show (z * 2))
 
checkPositive :: Int -> Maybe Int
checkPositive x = if x > 0 then Just x else Nothing
 
checkEven :: Int -> Maybe Int
checkEven x = if even x then Just x else Nothing

Question 3: Equational Reasoning and Functional Composition

(b) Assume that the list xs has a finite (but unbounded) number of elements. Prove that sum (map (2*) xs) = 2 * sum xs using the following definition of sum:

sum :: Num a => [a] -> a
sum [] = 0
sum (x:xs) = x + sum xs

Proof by induction over xs.

Base case: xs = [] sum (map (2*) []) = sum [] = 0 = 2*0 = 2 * sum []

Induction case: Assume sum (map (2*) xs) = 2 * sum xs holds for some xs.

Consider x:xs: sum (map (2*) (x:xs)) = sum ((2_x) : map (2_) xs) = (2_x) + sum (map (2_) xs) = (2*x) + 2 * sum xs (by induction hypothesis) = 2 * (x + sum xs) (by distributivity) = 2 * sum (x:xs) (by definition of sum)

Therefore, sum (map (2*) xs) = 2 * sum xs for all finite lists xs.

(d) Simplify the following lambda expression. Show each step in the reduction, and for each step show which conversion rule you are using.

(\a -> (\b -> a (b b))) (\x -> x) (\y -> y+1) 2

Question 4: Domain Specific Languages and Parsers

(b) Consider a type Color defined as follows:

data Color = Red | Green | Blue | Yellow | Purple | Custom Int Int Int

Write a function blend :: Color Color Color that blends two colors according to these rules:

  • Blending primary colors (Red, Green, Blue) creates secondary colors (Yellow, Purple, Cyan)
  • Blending a primary color with itself returns the same color
  • Blending Custom colors averages their RGB components
  • Other combinations result in a suitable Custom color

(c) Using the Parsec library, write a parser for a simple language of arithmetic expressions that supports addition, subtraction, multiplication, division, and parentheses. The parser should return an abstract syntax tree represented by the following data type:

data Expr = Num Int
         | Add Expr Expr
         | Sub Expr Expr
         | Mul Expr Expr
         | Div Expr Expr
         deriving (Show)
Link to original

FP Replica Past Paper 2

Question 1: Higher-Order Functions and Type Classes

(d) Recall that the Functor typeclass is defined as follows:

class Functor f where
   fmap :: (a -> b) -> f a -> f b

Write a Functor instance for the following binary tree data type:

data Tree a = Leaf | Node a (Tree a) (Tree a)

(e) Write a Functor instance for the following data type:

data Either' a b = Left' a | Right' b

Is this a valid Functor instance? Explain why or why not.

Question 2: Algebraic Data Types and Pattern Matching

(a) Given the following type and expression:

data Tree a = Leaf a | Branch (Tree a) (Tree a)
 
tree1 :: Tree Int
tree1 = Branch (Branch (Leaf 1) (Leaf 2)) (Leaf 3)

Write a function countLeaves :: Tree a Int that returns the number of Leaf nodes in a tree. For instance, countLeaves tree1 should evaluate to 3.

(e) Consider a binary search tree (BST) implemented with the following data type:

data BST a = Empty | Node a (BST a) (BST a)

Write a function insert :: Ord a => a -> BST a -> BST a that inserts a value into a BST, maintaining the binary search tree property.

Question 3: Monads and Effects

(a) The State monad allows for stateful computations while maintaining referential transparency. It is defined as follows:

newtype State s a = State { runState :: s -> (a, s) }
 
instance Monad (State s) where
   return x = State $ \s -> (x, s)
   m >>= f = State $ \s -> 
               let (a, s') = runState m s
               in runState (f a) s'

Define the following helper functions for the State monad:

get :: State s s            -- Retrieves the current state
put :: s -> State s ()      -- Replaces the state
modify :: (s -> s) -> State s ()  -- Applies a function to the state

(b) Use the State monad to implement a function evalRPN that evaluates a Reverse Polish Notation expression. RPN expressions are represented as lists of tokens, where a token can be either a number or an operator (+, -, _, /). For example, evalRPN [5, 3, "+", 2, "_"] should evaluate to 16.

data Token = Number Int | Plus | Minus | Times | Divide
 
evalRPN :: [Token] -> Maybe Int

Question 4: Lazy Evaluation and Infinite Data Structures

representing the Fibonacci sequence [0, 1, 1, 2, 3, 5, 8, ...] using lazy evaluation.

Answer:

fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

I’ll continue with the rest of the Replica Past Paper 2.

that returns an infinite list of prime numbers using the Sieve of Eratosthenes algorithm.

Answer:

primes :: [Integer]
primes = sieve [2..]
 where
   sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]

(d) Consider the following definition of an infinite binary tree:

data InfTree a = Node a (InfTree a) (InfTree a)
 
-- Creates an infinite tree where each node contains its path
-- represented as a list of Bools (False = left, True = right)
pathTree :: InfTree [Bool]
pathTree = buildTree []
 where
   buildTree path = Node path 
                      (buildTree (path ++ [False])) 
                      (buildTree (path ++ [True]))

Write a function getPath :: [Bool] InfTree [Bool] [Bool] that retrieves the node value at the path specified by the first argument. For example, getPath [False, True] pathTree should return [False, True].

Answer

getPath :: [Bool] -> InfTree [Bool] -> [Bool]
getPath [] (Node val _ _) = val
getPath (False:ps) (Node _ left _) = getPath ps left
getPath (True:ps) (Node _ _ right) = getPath ps right

(f) Consider the following infinite data structure representing a game tree for tic-tac-toe:

data Player = X | O deriving (Show, Eq)
type Board = [[Maybe Player]]  -- 3x3 grid
data GameTree = GameTree Board Player [GameTree]

Explain how lazy evaluation makes it possible to define such a tree, and how pruning strategies can be implemented to explore only parts of the tree.

Link to original