This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Programming in Haskell
Solutions to Exercises
Graham HuttonUniversity of Nottingham
Contents
Chapter 1 - Introduction 1
Chapter 2 - First steps 3
Chapter 3 - Types and classes 4
Chapter 4 - Defining functions 5
Chapter 5 - List comprehensions 7
Chapter 6 - Recursive functions 9
Chapter 7 - Higher-order functions 13
Chapter 8 - Functional parsers 15
Chapter 9 - Interactive programs 19
Chapter 10 - Declaring types and classes 21
Chapter 11 - The countdown problem 23
Chapter 12 - Lazy evaluation 24
Chapter 13 - Reasoning about programs 26
Chapter 1 - Introduction
Exercise 1
double (double 2)= { applying the inner double }
double (2 + 2)= { applying double }
(2 + 2) + (2 + 2)= { applying the first + }
4 + (2 + 2)= { applying the second + }
4 + 4= { applying + }
8
or
double (double 2)= { applying the outer double }
(double 2) + (double 2)= { applying the second double }
In general, checking if two functions are equal requires enumerating all possible ar-gument values, and checking if the functions give the same result for each of thesevalues. For functions with a very large (or infinite) number of argument values, suchas values of type Int or Integer , this is not feasible. However, for small numbers ofargument values, such as values of type of type Bool , it is feasible.
comment = do string "--"many (sat (= ’\n’))return ()
Exercise 3
(1)expr
������
��������
expr
������
������
��
+ expr
��expr
��
+ expr
��
term��
term��
term��
factor
��factor
��
factor��
nat��
nat��
nat��
4
2 3
(2)expr
�����������
���
expr
��
+ expr
������
������
��term
��
expr
��
+ expr
��factor
��
term��
term��
nat��
factor��
factor��
2 nat��
nat��
3 4
15
Exercise 4
(1)expr
������
�� ������
term��
+ expr
��factor
��
term��
nat��
factor��
2 nat��3
(2)expr
��term
������
��������
factor
��
∗ term����
�������
��
��nat
��
factor
��
∗ term��
2 nat��
factor��
3 nat��4
(3)expr
�������� ������
term��
+ expr
��factor
�������� �� �����
���term
��( expr
�������
�� �������
) factor
��term
��
+ expr
��
nat��
factor
��
term��
4
nat��
factor��
2 nat��3
16
Exercise 5
Without left-factorising the grammar, the resulting parser would backtrack excessivelyand have exponential time complexity in the size of the expression. For example, anumber would be parsed four times before being recognised as an expression.
Exercise 6
expr = do t ← termdo symbol "+"
e ← exprreturn (t + e)
+++ do symbol "-"e ← exprreturn (t − e)
+++ return t
term = do f ← factordo symbol "*"
t ← termreturn (f ∗ t)
+++ do symbol "/"t ← termreturn (f ‘div ‘ t)
+++ return f
Exercise 7
(1)
factor ::= atom (↑ factor | epsilon)
atom ::= (expr) | nat
(2)
factor :: Parser Intfactor = do a ← atom
do symbol "^"f ← factorreturn (a ↑ f )
+++ return a
atom :: Parser Intatom = do symbol "("
e ← exprsymbol ")"return e
+++ natural
17
Exercise 8
(a)
expr ::= expr − nat | nat
nat ::= 0 | 1 | 2 | · · ·(b)
expr = do e ← exprsymbol "-"n ← naturalreturn (e − n)
+++ natural
(c)
The parser loops forever without producing a result, because the firstoperation it performs is to call itself recursively.
(d)
expr = do n ← naturalns ← many (do symbol "-"
natural)return (foldl (−) n ns)
18
Chapter 9 - Interactive programs
Exercise 1
readLine = get ""
get xs = do x ← getCharcase x of
’\n’→ return xs’\DEL’→ if null xs then
get xselse
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 :: Boardinitial = [5, 4, 3, 2, 1]
finished :: Board → Boolfinished b = all (== 0) b
valid :: Board → Int → Int → Boolvalid b row num = b !! (row − 1) ≥ num
move :: Board → Int → Int → Boardmove b row num = [if r == row then n − num else n
| (r ,n)← zip [1 . . 5] b ]newline :: IO ()newline = putChar ’\n’
19
putBoard :: Board → IO ()putBoard [a, b, c, d , e ] = do putRow 1 a
putRow 2 bputRow 3 cputRow 4 dputRow 5 e
putRow :: Int → Int → IO ()putRow row num = do putStr (show row)
putStr ": "putStrLn (stars num)
stars :: Int → Stringstars n = concat (replicate n "* ")
data Prop = · · · | Or Prop Prop | Equiv Prop Prop
eval s (Or p q) = eval s p ∨ eval s qeval s (Equiv p q) = eval s p == eval s q
vars (Or p q) = vars p ++ vars qvars (Equiv p 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 :: Expr → Cont → Inteval (Val n) ops = exec ops neval (Add x y) ops = eval x (EVALA y : ops)eval (Mult x y) ops = eval x (EVALM y : ops)
exec :: Cont → Int → Intexec [ ] n = nexec (EVALA y : ops) n = eval y (ADD n : ops)exec (ADD n : ops) m = exec ops (n + m)exec (EVALM y : ops) n = eval y (MUL n : ops)exec (MUL n : ops) m = exec ops (n ∗m)
value :: Expr → Intvalue e = eval e [ ]
Exercise 8
instance Monad Maybe wherereturn :: a → Maybe areturn x = Just x
(>>=) :: Maybe a → (a → Maybe b)→ Maybe bNothing >>= = Nothing(Just x ) >>= f = f x
instance Monad [ ] wherereturn :: a → [a ]return x = [x ]
(>>=) :: [a ]→ (a → [b ])→ [b ]xs >>= f = 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
foldr1 :: (a → a → a)→ [a ]→ afoldr1 [x ] = xfoldr1 f (x : xs) = 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 5.1
Base case:
[ ] ++ [ ]= { applying ++ }
[ ]
Inductive case:
(x : xs) ++ [ ]= { applying ++ }
x : (xs ++ [ ])= { induction hypothesis }
x : xs
27
Exercise 5.2
Base case:
[ ] ++ (ys ++ zs)= { applying ++ }
ys ++ zs= { unapplying ++ }
([ ] ++ ys) ++ zs
Inductive case:
(x : xs) ++ (ys ++ zs)= { applying ++ }
x : (xs ++ (ys ++ zs))= { induction hypothesis }
x : ((xs ++ ys) ++ zs)= { unapplying ++ }
(x : (xs ++ ys)) ++ zs= { unapplying ++ }
((x : xs) ++ ys) ++ zs
Exercise 6
The three auxiliary results are all general properties that may be useful in othercontexts, 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 ) = 1leaves (Node l r) = leaves l + leaves r
nodes (Leaf ) = 0nodes (Node l r) = 1 + nodes l + nodes r
Property:
leaves t = nodes t + 1
Base case:
nodes (Leaf n) + 1= { applying nodes }
0 + 1= { applying + }
1= { unapplying leaves }
leaves (Leaf n)
29
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 = PUSH n : ccomp′ (Add x y) c = comp′ x (comp′ y (ADD : c))