| Elements of ML Programming, 2nd
Edition (ML97)
|
Solutions for Chapter 6
Solutions for Section 6.1
Solutions for Section 6.2
Solutions for Section 6.3
Solutions for Section 6.4
Solutions for Section 6.1
Exercise 6.1.1(a)
type 'a setOfSets = 'a list list;
Exercise 6.2.1
Node(1, Node(2,Empty,Empty), Node(3,Empty,Empty)) is one possible
answer.
Exercise 6.2.2
Define the type by
type ('d, 'r) mapTree = ('d * 'r) btree;
Then, we can define the desired tree by
val t1 = Node(("a",1), Empty, Empty): (string, int) mapTree;
Exercise 6.2.4
open TextIO;
datatype intOrEof = Eof | Int of int;
fun digit(c) = c >= #"0" andalso c <= #"9";
fun startInt(file) = startInt1(file, input1(file))
and startInt1(file, NONE) = Eof
| startInt1(file, SOME c) =
if digit(c) then Int(ord(c)-ord(#"0"))
else startInt(file);
fun finishInt(i,file) =
if i = Eof then Eof
else finishInt1(i,file,input1(file))
and finishInt1(i, file, NONE) = i
| finishInt1(i as Int(j), file, SOME c) =
if digit(c) then
finishInt(Int(10*j+ord(c)-ord(#"0")), file)
else i;
fun getInt(file) = finishInt(startInt(file), file)
fun sumInts1(file) =
let
val i = getInt(file)
in
if i = Eof then 0
else let
val Int(j) = i
in
j + sumInts1(file)
end
end;
fun sumInts(filename) = sumInts1(openIn(filename));
Download this program
Exercise 6.2.5(a)
datatype suit = Club | Heart | Diamond | Spade;
Exercise 6.2.5(c)
We need a recursive datatype with a basis data constructor, say
Int, to wrap integers, and an inductive data constructor, say
Thing, to wrap a list of things.
An appropriate declaration is
datatype thing =
Int of int |
Thing of thing list
Here is a typical value of the datatype thing.
Thing([Int(1), Thing([Int(2)]), Int(3)]);
Exercise 6.2.7
type 'node graph = ('node * 'node list) list;
exception NotANode;
(* succ(a,G) looks up the list of successors of
node a in graph G *)
fun succ(a,nil) = raise NotANode
| succ(a,(b,L)::tail) =
if a=b then L else succ(a,tail);
(* member(a,L) determines whether a is on list L *)
fun member(a,nil) = false
| member(a,b::bs) =
if a=b then true else member(a,bs);
(* search1(L,R,G) finds all the nodes reachable from any
of the list of nodes L in graph G, but does not search
from the set R of nodes that have already been reached.
R is included in the set of reached nodes that is
eventually returned. *)
fun search1(nil,R,G) = R
| search1(x::xs,R,G) =
if member(x,R) then search1(xs,R,G)
else (* x is a new node, never before seen *)
search1(succ(x,G)@xs, x::R, G);
(* search(a,G) finds the set of nodes reachable from
a in graph G *)
fun search(a,G) = search1([a],nil,G);
Download this program
Return to Top
Solutions for Section 6.3
Exercise 6.3.2(a)
fun lookup lt Empty a = raise Missing
| lookup lt (Node((c,b),left,right)) a =
if lt(a,c) then lookup lt left a
else if lt(c,a) then lookup lt right a
else (* a=c *) b;
Exercise 6.3.3(a)
val lookup1 = lookup (op < : real*real->bool);
and similarly for the other two functions.
An alternative is
val lookup1 = lookup (fn (x:real,y) => x<y);
using an anonymous function.
However, beware
val lookup1 = lookup (op <:real*real->bool);
which, although it looks almost identical to our first solution, is an
error.
Do you see why?
Exercise 6.3.4
The idea is to form the set of integers into as balanced a binary tree
as we can.
The longest paths will then be proportional to the logarithm of the
size of S.
Here is an example where the set of integers is 1, 4, 9, 16, 25, and 36.
We leave as an interesting challenge a function that will take a
list of integers and form them into a binary search tree that is as
balanced as possible.
val lookupS = lookup (op <) (
Node(16,
Node(4,
Node(1,Empty,Empty),
Node(9,Empty,Empty)
),
Node(36,
Node(25,Empty,Empty),
Empty
)
)
);
Download this program
Return to Top
Solutions for Section 6.4
Exercise 6.4.1
Here is a solution to part (a).
We test whether x is at the root, and if not, test whether it is
either in the first subtree or in the tree obtained by removing the
first subtree.
fun isOn (Node(a,nil)) x = (a=x)
| isOn (Node(a,t::ts)) x =
isOn t x orelse isOn (Node(a,ts)) x;
For part (b), we found it more convenient to put the element x
first among the arguments.
Then, the following works:
fun isOn x (Node(a,L)) =
(a=x) orelse
foldr (fn (x,y) => x orelse y) false
(map (isOn x) L);
If x is required to be the second argument, then we need to
define another anonymous function to take the place of isOn x,
which is a function that takes a tree as argument and tells whether
x is on that tree.
Note that in the above code, (map (isOn x) L) returns a list of
booleans, each telling whether x is on one of the subtrees in the
list L.
Then, foldr is applied to the function that takes the OR of its
arguments, an initial value false, and the list of booleans
that resulted from the application of map.
Exercise 6.4.4(a)
fun preOrder(Node(a,nil)) = [a]
| preOrder(Node(a,t::ts)) =
a :: preOrder(t) @ (tl(preOrder(Node(a,ts))));
Exercise 6.4.6
fun preOrder1(Node(a,nil), L) = a::L
| preOrder1(Node(a,t::ts), L) =
a :: preOrder1(t, tl(preOrder1(Node(a,ts),L)));
fun preOrder(T) = preOrder1(T,nil);
Function preOrder1 takes a tree and a list of labels L
that must be appended to the preorder of the tree.
If the tree is a single node, then the result is the label a of
that node followed by the list L.
If the root has one or more subtrees, a call to preOrder1
computes the preorder listing of the tree with the first subtree
removed, followed by the list L.
We use tl to remove the premature occurrence of the root label
a from this list.
The list then becomes the second argument of another call to
preOrder1, which applies to the first subtree and results in
the preorder listing of that subtree followed by the preorder listings
of all the other subtrees, and then the list L.
Since a is put on the front of this list, preOrder1
correctly returns the preorder listing of its tree argument followed by
its second (list) argument.
Return to Top