Exercise 2
[a ] → a
(a, b) → (b, a)
a → b → (a, b)
Num a ⇒ a → a
Eq a ⇒ [a ] → Bool
(a → a) → a → a
Exercise 3
No solution required.
Exercise 4
In general, checking if two functions are equal requires enumerating all possible argument values, and checking if the functions give the same result for each of these
values. For functions with a very large (or infinite) number of argument values, such
as values of type Int or Integer, this is not feasible. However, for small numbers of
argument values, such as values of type of type Bool , it is feasible.
(2)
drop 3 [1, 2, 3, 4, 5]
{ applying drop
drop 2 [2, 3, 4, 5]
=
{ applying drop
drop 1 [3, 4, 5]
=
{ applying drop
drop 0 [4, 5]
=
{ applying drop
[4, 5]
Exercise 5
Without left-factorising the grammar, the resulting parser would backtrack excessively
and have exponential time complexity in the size of the expression. For example, a
number would be parsed four times before being recognised as an expression.
Exercise 6
expr
= do t ← term
do symbol "+"
e ← expr
return (t + e)
+++ do symbol "-"
e ← expr
return (t − e)
+++ return t
term
= do f ← factor
do symbol "*"
t ← term
return (f ∗ t )
+++ do symbol "/"
t ← term
return (f ‘div ‘ t )
+++ return f
Exercise 7
(1)
factor
::=
atom (↑ factor | epsilon)
atom
::=
(expr ) | nat
factor
factor
:: Parser Int
= do a ← atom
do symbol "^"
f ← factor
return (a ↑ f )
+++ return a
(2)
atom
atom
::
=
Parser Int
do symbol "("
e ← expr
symbol ")"
return e
+++ natural
17
Exercise 8
(a)
expr
::=
expr − nat | nat
nat
::=
0 | 1 | 2 | ···
expr
= do e ← expr
symbol "-"
n ← natural
return (e − n)
+++ natural
(b)
(c)
The parser loops forever without producing a result, because the first
operation it performs is to call itself recursively.
(d)
expr
= do n ← natural
ns ← many (do symbol "-"
natural)
return (foldl (−) n ns)
= do x ← getChar
case x of
’\n’ → return xs
’\DEL’ → if null xs then
get xs
else
do putStr "\ESC[1D \ESC[1D"
get (init xs)
→ get (xs ++ [x ])
Exercise 2
No solution available.
Exercise 3
No solution available.
Exercise 4
No solution available.
Exercise 5
No solution available.
Exercise 6
type Board
=
[Int ]
initial
initial
::
=
Board
[5, 4, 3, 2, 1]
finished
finished b
::
=
Board → Bool
all (== 0) b
valid
valid b row num
::
=
Board → Int → Int → Bool
b !! (row − 1) ≥ num
move
move b row num
::
=
newline
newline
::
=
Board → Int → Int → Board
[if r == row then n − num else n
| (r , n) ← zip [1 . . 5] b ]
IO ()
putChar ’\n’
19
putBoard
putBoard [a, b, c, d , e ]
::
=
Board → IO ()
do putRow 1 a
putRow 2 b
putRow 3 c
putRow 4 d
putRow 5 e
putRow
putRow row num
::
=
Int → Int → IO ()
do putStr (show row )
putStr ": "
putStrLn (stars num)
stars
stars n
::
=
Int → String
concat (replicate n "* ")
getDigit
getDigit prom
::
=
String → IO Int
do putStr prom
x ← getChar
newline
if isDigit x then
return (ord x − ord ’0’)
else
do putStrLn "ERROR: Invalid digit"
getDigit prom
nim
nim
::
=
IO ()
play initial 1
play
play board player
::
=
Board → Int → IO ()
do newline
putBoard board
if finished board then
do newline
putStr "Player "
putStr (show (next player ))
putStrLn " wins!!"
else
do newline
putStr "Player "
putStrLn (show player )
r ← getDigit "Enter a row number: "
n ← getDigit "Stars to remove : "
if valid board r n then
play (move board r n) (next player )
else
do newline
putStrLn "ERROR: Invalid move"
play board player
next
next 1
next 2
::
=
=
Int → Int
2
1
20
Chapter 10 - Declaring types and classes
Exercise 1
mult m Zero
mult m (Succ n)
= Zero
= add m (mult m n)
Exercise 2
occurs m (Leaf n)
=
occurs m (Node l n r ) =
m == n
case compare m n of
LT → occurs m l
EQ → True
GT → occurs m r
This version is more efficient because it only requires one comparison for each node,
whereas the previous version may require two comparisons.
Exercise 3
leaves (Leaf )
leaves (Node l r )
=
=
balanced (Leaf )
=
balanced (Node l r ) =
1
leaves l + leaves r
True
abs (leaves l − leaves r ) ≤ 1
∧ balanced l ∧ balanced r
Exercise 4
halve xs
=
balance [x ] =
balance xs =
splitAt (length xs ‘div ‘ 2) xs
Leaf x
Node (balance ys) (balance zs)
where (ys, zs) = halve xs
Exercise 5
data Prop
= · · · | Or Prop Prop | Equiv Prop Prop
eval s (Or p q)
eval s (Equiv p q)
= eval s p ∨ eval s q
= eval s p == eval s q
vars (Or p q)
vars (Equiv p q)
= vars p ++ vars q
= vars p ++ vars q
Exercise 6
No solution available.
21
Exercise 7
data Expr
= Val Int | Add Expr Expr | Mult Expr Expr
type Cont
= [Op ]
data Op
= EVALA Expr | ADD Int | EVALM Expr | MUL Int
eval
eval (Val n) ops
eval (Add x y) ops
eval (Mult x y) ops
::
=
=
=
Expr → Cont → Int
exec ops n
eval x (EVALA y : ops)
eval x (EVALM y : ops)
exec
exec
exec
exec
exec
exec
::
=
=
=
=
=
Cont → Int → Int
n
eval y (ADD n : ops)
exec ops (n + m)
eval y (MUL n : ops)
exec ops (n ∗ m)
[] n
(EVALA y : ops) n
(ADD n : ops) m
(EVALM y : ops) n
(MUL n : ops) m
:: Expr → Int
= eval e [ ]
value
value e
Exercise 8
instance Monad Maybe where
return
:: a → Maybe a
return x
= Just x
(>
>=)
Nothing >
>=
(Just x ) >
>= f
:: Maybe a → (a → Maybe b) → Maybe b
= Nothing
= f x
instance Monad [ ] where
return
:: a → [a ]
return x = [x ]
(>
>=)
xs >
>= f
:: [a ] → (a → [b ]) → [b ]
= concat (map f xs)
22
Chapter 11 - The countdown problem
Exercise 1
choices xs
=
[zs | ys ← subs xs, zs ← perms ys ]
Exercise 2
removeone x [ ]
= []
removeone x (y : ys)
| x == y
= ys
| otherwise
= y : removeone x ys
isChoice [ ]
isChoice (x : xs) [ ]
isChoice (x : xs) ys
= True
= False
= elem x ys ∧ isChoice xs (removeone x ys)
Exercise 3
It would lead to non-termination, because recursive calls to exprs would no longer be
guaranteed to reduce the length of the list.
Chapter 12 - Lazy evaluation
Exercise 1
(1)
2 ∗ 3 is the only redex, and is both innermost and outermost.
(2)
1 + 2 and 2 + 3 are redexes, with 1 + 2 being innermost.
(3)
1 + 2, 2 + 3 and fst (1 + 2, 2 + 3) are redexes, with the first of these being
innermost and the last being outermost.
(4)
2 ∗ 3 and (λx → 1 + x ) (2 ∗ 3) are redexes, with the first being innermost
and the second being outermost.
takeTree
takeTree 0
takeTree (n + 1) Leaf
takeTree (n + 1) (Node l x r )
::
=
=
=
replicateTree
replicateTree n
:: Int → a → Tree a
= takeTree n ◦ repeatTree
Int → Tree a → Tree a
Leaf
Leaf
Node (takeTree n l ) x (takeTree n r )
25
Chapter 13 - Reasoning about programs
Exercise 1
last
::
last [x ]
=
last ( : xs) =
[a ] → a
x
last xs
init
::
=
init [ ]
init (x : xs) =
[a ] → [a ]
[]
x : init xs
or
or
foldr1
::
=
foldr1 [x ]
foldr1 f (x : xs) =
(a → a → a) → [a ] → a
x
f x (foldr1 f xs)
There are a number of other answers too.
Exercise 2
Base case:
add Zero (Succ m)
{ applying add }
Succ m
=
{ unapplying add }
Succ (add Zero m)
=
Inductive case:
add (Succ n) (Succ m)
{ applying add }
Succ (add n (Succ m))
=
{ induction hypothesis }
Succ (Succ (add n m))
=
{ unapplying add }
Succ (add (Succ n) m)
=
Exercise 3
Base case:
add Zero m
{ applying add }
m
=
{ property of add }
add m Zero
=
26
Inductive case:
add (Succ n) m
{ applying add }
Succ (add n m)
=
{ induction hypothesis }
Succ (add m n)
=
{ property of add }
add m (Succ n)
=
Exercise 4
Base case:
all (== x ) (replicate 0 x )
{ applying replicate }
all (== x ) [ ]
=
{ applying all }
True
=
Inductive case:
=
=
=
=
=
all (== x ) (replicate (n + 1) x )
{ applying replicate }
all (== x ) (x : replicate n x )
{ applying all }
x == x ∧ all (== x ) (replicate n x )
{ applying == }
True ∧ all (== x ) (replicate n x )
{ applying ∧ }
all (== x ) (replicate n x )
{ induction hypothesis }
True
Exercise 6
The three auxiliary results are all general properties that may be useful in other
contexts, whereas the single auxiliary result is specific to this application.
Exercise 7
Base case:
map f (map g [ ])
{ applying the inner map }
map f [ ]
=
{ applying map }
[]
=
{ unapplying map }
map (f ◦ g) [ ]
=
Inductive case:
=
=
=
=
=
map f (map g (x : xs))
{ applying the inner map }
map f (g x : map g xs)
{ applying the outer map }
f (g x ) : map f (map g xs)
{ induction hypothesis }
f (g x ) : map (f ◦ g) xs
{ unapplying ◦ }
(f ◦ g) x : map (f ◦ g) xs
{ unappling map }
map (f ◦ g) (x : xs)
28
Exercise 8
Base case:
take 0 xs ++ drop 0 xs
{ applying take, drop }
[ ] ++ xs
=
{ applying ++ }
xs
=
Base case:
take (n + 1) [ ] ++ drop (n + 1) [ ]
{ applying take, drop }
[ ] ++ [ ]
=
{ applying ++ }
[]
=
Inductive case:
take (n + 1) (x : xs) ++ drop (n + 1) (x : xs)
{ applying take, drop }
(x : take n xs) ++ (drop n xs)
=
{ applying ++ }
x : (take n xs ++ drop n xs)
=
{ induction hypothesis }
x : xs
=
Exercise 9
Definitions:
leaves (Leaf )
leaves (Node l r )
Inductive case:
nodes (Node l r ) + 1
{ applying nodes }
1 + nodes l + nodes r + 1
=
{ arithmetic }
(nodes l + 1) + (nodes r + 1)
=
{ induction hypotheses }
leaves l + leaves r
=
{ unapplying leaves }
leaves (Node l r )
=
Exercise 10
Base case:
comp (Val n) c
=
{ applying comp }
comp (Val n) ++ c
=
{ applying comp }
[PUSH n ] ++ c
=
{ applying ++ }
PUSH n : c
Inductive case:
=
=
=
=
=
=
comp (Add x y) c
{ applying comp }
comp (Add x y) ++ c
{ applying comp }
(comp x ++ comp y ++ [ADD ]) ++ c
{ associativity of ++ }
comp x ++ (comp y ++ ([ADD ] ++ c))
{ applying ++ }
comp x ++ (comp y ++ (ADD : c))
{ induction hypothesis for y }
comp x ++ (comp y (ADD : c))
{ induction hypothesis for x }
comp x (comp y (ADD : c))
In conclusion, we obtain:
comp (Val n) c
comp (Add x y) c