b b
Categories and Subject Descriptors
D.1.1 [Programming Te
hniques℄: Appli
ative (Fun
 Laun
hbury knew of a solution by Jones and Gibbons [5℄
tional) Programming that depended on lazy evaluation, but wondered how one
would solve the problem in a stri
t language like Standard
ML [6℄. I qui
kly s
ribbled what seemed to me to be a
General Terms mostly straightforward answer and showed it to him at the
next break.
Algorithms, Design Over the next year, I presented the problem to many other
fun
tional programmers and was
ontinually amazed at the
Keywords baroque solutions I re
eived in reply. With only a single
ex
eption, everyone who
ame near a workable answer went
Breadthrst numbering, breadthrst traversal, views in a very dierent dire
tion from my solution right from the
very beginning of the design pro
ess. I gradually realized
that I was witnessing some sort of mass mental blo
k, a
1. INTRODUCTION
ommunal blind spot, that was steering programmers away
Breadthrst traversal of a tree is easy, but rebuilding the from what seemed to be a very natural solution. I make no
tree afterwards seems to be mu
h harder, at least to fun

laims that mine is the best solution, but I nd it fas
inating
tional programmers. At ICFP'98, John Laun
hbury
hal that something about my solution makes it so diÆ
ult for
lenged me with the following problem: fun
tional programmers to
on
eive of in the rst pla
e.
Given a tree T ,
reate a new tree of the same
shape, but with the values at the nodes repla
ed STOP!
by the numbers 1 : : : jT j in breadthrst order.
Before reading further, spend ten or fteen minutes sket
h
For example, breadthrst numbering of the tree ing out a solution. For
on
reteness, assume that you have
a type of labeled binary trees
a
datatype 'a Tree = E
b d  T of 'a * 'a Tree * 'a Tree
b b b
and that you are to produ
e a fun
tion
b b
bfnum : 'a Tree > int Tree
Permission to make digital or hard copies of all or part of this work for
2. BREADTHFIRST TRAVERSAL
personal or classroom use is granted without fee provided that copies are When attempting to solve any nontrivial problem, the
not made or distributed for profit or commercial advantage and that copies rst step should always be to review solutions to related
bear this notice and the full citation on the first page. To copy otherwise, to problems. In algorithm design, as in programming in gen
republish, to post on servers or to redistribute to lists, requires prior specific
permission and/or a fee.
eral, theft of ideas is to be applauded rather than
on
ICFP’00, Montreal, Canada demned. In this
ase, the most obvious
andidate for plun
Copyright 2000 ACM 1581132026/00/0009 ..$5.00 der is the wellknown queuebased algorithm for breadthrst
traversal, that is, produ
ing a list of the labels in a tree, in
breadthrst order [5℄. For example, breadthrst traversal signature QUEUE =
of the tree sig
a
type 'a Queue
val empty : 'a Queue
b d val isEmpty : 'a Queue > bool
b
b b
b b
val enq : 'a Queue * 'a > 'a Queue
val deq : 'a Queue > 'a * 'a Queue
should yield the list [a; b; d;
℄. end
The key step in developing an algorithm for breadthrst
traversal is to generalize the problem, illustrating the para signature BFTRAV =
doxi
al, yet
ommon, phenomenon that a more general prob sig
lem is often easier to solve. In parti
ular, we generalize the val bftrav : 'a Tree > 'a list
problem from breadthrst traversal of a tree to breadth end
rst traversal of a forest, that is, from
bftrav : 'a Tree > 'a list fun
tor BreadthFirstTraversal (Q:QUEUE) : BFTRAV =
stru
t
to open Q
bftrav' : 'a Tree Seq > 'a list
fun bftrav' q =
where Seq is some asyetundetermined type of sequen
es if isEmpty q then [℄
used to represent forests. For example, breadthrst traver else
ase deq q of
sal of the forest (E, ts) => bftrav' ts
 (T (x,a,b), ts) =>
a d e x :: bftrav' (enq (enq (ts,a),b))
b
b b b f
b b b b b b
fun bftrav t = bftrav' (enq (empty, t))
end
should yield the list [a; d; e; b;
; f ℄.
Then, bftrav
an be spe
ied by the equation
bftrav t = bftrav' hti Figure 1: Breadthrst traversal in SML.
where hti denotes the singleton forest
ontaining t.
Now, bftrav' is easy to spe
ify with the following three
equations:
bftrav' h i = [℄ signature QUEUE =
bftrav' (E ts) = bftrav' ts sig
bftrav' (T (x,a,b) ts) = type 'a Queue
x :: bftrav' (ts a b) val empty : 'a Queue
val >> : 'a Queue * 'a > 'a Queue
The last equation takes the
hildren of the rst tree and
adds them to the end of the sequen
e. The empty sequen
e viewtype 'a Queue = Empty  << of 'a * 'a Queue
is denoted h i, and the symbols and denote inx \
ons" end
and \sno
", respe
tively. Sin
e this is a spe
i
ation rather
than an implementation, I feel free to use h i, , and on fun
tor BreadthFirstTraversal (Q:QUEUE) : BFTRAV =
both sides of the equations. stru
t
The nal step before a
tually produ
ing
ode is to
hoose open Q
an implementation for the sequen
e ADT. The main opera infix >>
tions we need on these sequen
es are adding trees to the end infixr <<
of the sequen
e and removing trees from the beginning of
the sequen
e. Therefore, we
hoose queues as our sequen
e fun bftrav' Empty = [℄
representation. Figure 1 gives a
on
rete implementation in  bftrav' (E << ts) = bftrav' ts
Standard ML.  bftrav' (T (x,a,b) << ts) =
The use of queues as an ADT makes this
ode look rather x :: bftrav' (ts >> a >> b)
ugly to an eye a
ustomed to the
leanliness of pattern
mat
hing, espe
ially the ifthenelse and
ase expressions fun bftrav t = bftrav' (empty >> t)
in bftrav'. The problem is that pattern mat
hing
annot end
normally be performed on ADTs. Views [10℄ oer a way
around this problem. Figure 2 reimplements breadthrst
traversal more
leanly using the syntax for views proposed Figure 2: Breadthrst traversal using views.
in [9℄. Note that the denition of bftrav' is now nearly
identi
al to the spe
i
ation.
Provided ea
h queue operation runs in O(1) time, this
algorithm runs in O(n) time altogether. A good implemen
tation of queues for this appli
ation would be the usual im
plementation as a pair of lists [1, 2, 3℄. Sin
e this appli
ation signature BFNUM =
does not require persisten
e, fan
ier kinds of queues (e.g., [3, sig
7℄) would be overkill. val bfnum : 'a Tree > int Tree
end
3. BREADTHFIRST NUMBERING
We next attempt to extend the solution to breadthrst fun
tor BreadthFirstNumbering (Q:QUEUE) : BFNUM =
traversal to get a solution to breadthrst numbering. As stru
t
in breadthrst traversal, we will begin by generalizing the open Q
problem. Instead of breadthrst numbering of a tree, we
will
onsider breadthrst numbering of a forest. In other fun bfnum' i q =
words, we introdu
e a helper fun
tion that takes a forest and if isEmpty q then empty
returns a numbered forest of the same shape. It will also be else
ase deq q of
helpful for the helper fun
tion to take the
urrent index, so (E, ts) => enq (bfnum' i ts, E)
its signature will be  (T (x,a,b), ts) =>
let val q = enq (enq (ts, a), b)
bfnum' : int > 'a Tree Seq > int Tree Seq val q' = bfnum' (i+1) q
Then bfnum
an be spe
ied in terms of bfnum' as val (b', q'') = deq q'
val (a', ts') = deq q''
bfnum t = t' in enq (ts', T (i,a',b')) end
where ht'i = bfnum' 1 hti
Extending the equations for bftrav' to bfnum' is fairly fun bfnum t =
straightforward, remembering that the output forest must let val q = enq (empty, t)
always have the same shape as the input forest. val q' = bfnum' 1 q
val (t',_) = deq q'
bfnum' i h i = h i in t' end
bfnum' i (E ts) = E ts' end
where ts' = bfnum' i ts
bfnum' i (T (x,a,b) ts) = T (i,a',b') ts'
where ts' a' b' = bfnum' (i+1) (ts a b) Figure 3: Breadthrst numbering in SML.
Noti
e how every equation textually preserves the shape of
the forest.
Given these spe
i
ations, we need to
hoose a representa
tion for sequen
es. The main operations we need on forests
are adding and removing trees at both the front and the
ba
k. Therefore, we
ould
hoose doubleended queues as
our sequen
e representation (perhaps using Hoogerwoord's
implementation of doubleended queues [4℄). However, a fun
tor BreadthFirstNumbering (Q:QUEUE) : BFNUM =
loser inspe
tion reveals that we treat the input forest and stru
t
the output forest dierently. In parti
ular, we add trees to open Q
the ba
k of input forests and remove them from the front, infixr <<
whereas we add trees to the front of output forests and re infix >>
move them from the ba
k. If we remove the arti
ial
on
straint that input forests and output forests should be rep fun bfnum' i Empty = empty
resented with the same kind of sequen
e, then we
an repre  bfnum' i (E << ts) = bfnum' i ts >> E
sent input forests as ordinary queues and output forests as  bfnum' i (T (x,a,b) << ts) =
ba
kwards queues. let val b' << a' << ts' =
If we want to represent both input forests and output bfnum' (i+1) (ts >> a >> b)
forests as ordinary queues (perhaps be
ause our library doesn't in ts' >> T (i,a',b') end
in
lude ba
kwards queues), then we
an
hange the spe
i
ation of bfnum' to return the numbered forest in reverse fun bfnum t =
order. Then, the equations be
ome let val t' << Empty = bfnum' 1 (empty >> t)
bfnum' i h i = h i in t' end
bfnum' i (E ts) = ts' E end
where ts' = bfnum' i ts
bfnum' i (T (x,a,b) ts) = ts' T (i,a',b')
where b' a' ts' = bfnum' (i+1) (ts a b) Figure 4: Breadthrst numbering using views.
Now it is a simple matter to turn this spe
i
ation into
running
ode, either with views (Figure 4) or without (Fig
ure 3). Either way, assuming ea
h queue operation runs in
O (1) time, the entire algorithm runs in O(n) time. On
e stru
ture BreadthFirstNumberingByLevels : BFNUM =
again, the usual implementation of queues as a pair of lists stru
t
would be a good
hoi
e for this algorithm. fun
hildren E = [℄

hildren (T (x,a,b)) = [a,b℄
4. LEVELORIENTED SOLUTIONS fun rebuild i [℄ [℄ = [℄
Nearly all the alternative solutions I re
eived from other  rebuild i (E :: ts)
s = E :: rebuild i ts
s
fun
tional programmers are level oriented, meaning that they  rebuild i (T (_,_,_) :: ts) (a :: b ::
s) =
expli
itly pro
ess the tree (or forest) level by level. In
on T (i,a,b) :: rebuild (i+1) ts
s
trast, my queuebased solutions do not make expli
it the
transition from one level to the next. The main advantage of fun bfnum' i [℄ = [℄
the leveloriented approa
h is that it relies only on lists, not  bfnum' i lvl =
on fan
ier data stru
tures su
h as queues or doubleended let val nextLvl =
on
at (map
hildren lvl)
queues. val j = i + (length nextLvl div 2)
I will not attempt to des
ribe all the possible leveloriented val nextLvl' = bfnum' j nextLvl
solutions. Instead, to provide a fair
omparison to my queue in rebuild i lvl nextLvl' end
based approa
h, I will des
ribe only the
leanest of these de
signs. (For
ompleteness, I also review Jones and Gibbons' fun bfnum t = hd (bfnum' 1 [t℄)
algorithm in Appendix A, but their algorithm is not dire
tly end
omparable to mine sin
e it depends on lazy evaluation.)
Given a list of trees, where the roots of those trees form
the
urrent level, we
an extra
t the next level by
olle
ting Figure 5: Leveloriented breadthrst numbering.
the subtrees of any nonempty nodes in the
urrent level, as
in
to prefer one over the other. The leveloriented solution
on
at (map
hildren lvl) is perhaps slightly easier to design from s
rat
h, but the
queuebased algorithm is only a modest extension of the
where queuebased algorithm for breadthrst traversal, whi
h is
fun
hildren E = [℄
quite wellknown (more wellknown, in fa
t, than the level

hildren (T (x,a,b)) = [a,b℄
oriented algorithm for breadthrst traversal). Informal tim
ings indi
ate that the leveloriented solution to breadthrst
Later, after a re
ursive
all has numbered all the trees in numbering is slightly faster than the queuebased one, but
the next level, we
an number the
urrent level by walking the dieren
e is minor and is not in any
ase an a priori
down both lists simultaneously, taking two numbered trees justi
ation for favoring the leveloriented approa
h.
from the next level for every nonempty node in the
urrent Why is it then that fun
tional programmers fa
ed with
level. this problem so overwhelmingly
ommit to a leveloriented
approa
h right from the beginning of the design pro
ess?
fun rebuild i [℄ [℄ = [℄ I
an only spe
ulate, armed with ane
dotal responses from
 rebuild i (E :: ts)
s = E :: rebuild i ts
s those programmers who have attempted the exer
ise. I have
 rebuild i (T (_,_,_) :: ts) (a :: b ::
s) = identied four potential explanations:
T (i,a,b) :: rebuild (i+1) ts
s
Unfamiliarity with the underlying traversal algorithm.
The last tri
ky point is how to
ompute the starting index A programmer unfamiliar with the queuebased algo
for numbering the next level from the starting index for the rithm for breadthrst traversal would be ex
eedingly
urrent level. We
annot simply add the length of the list unlikely to
ome up with the queuebased algorithm
representing the
urrent level to the
urrent index, be
ause for breadthrst numbering. However, this a
ounts
the
urrent level may
ontain arbitrarily many empty nodes, for only a small fra
tion of parti
ipants in the exer
whi
h should not in
rease the index. Instead, we need to
ise.
nd the number of nonempty nodes in the
urrent level.
Although we
ould dene a
ustom fun
tion to
ompute that Unfamiliarity with fun
tional queues and doubleended
value, we
an instead noti
e that ea
h nonempty node in queues. A programmer unfamiliar with the fa
t that
the
urrent level
ontributes two nodes to the next level, and su
h data stru
tures
an be implemented fun
tionally
therefore merely divide the length of the next level by two. would be unlikely to design an algorithm that required
The
omplete algorithm appears in Figure 5. their use. In this
ategory, I perhaps have an un
This algorithm makes three passes over ea
h level, rst fair advantage, having invented a variety of new im
omputing its length, then
olle
ting its
hildren, and nally plementations of fun
tional queues and doubleended
rebuilding the level. At the pri
e of slightly messier
ode, we queues [8℄. But most programmers profess an aware
ould easily
ombine the rst two passes, but there seems ness that these data stru
tures are available othe
to be no way to a
omplish all three tasks in a single pass shelf, even if they
ouldn't say ohand how those im
without lazy evaluation. plementations worked.
Premature
ommitment to a data stru
ture. Most fun

5. DISCUSSION tional programmers immediately rea
h for lists, and
Comparing my queuebased solution with the leveloriented try something fan
ier only if they get stu
k. Even the
solution in the previous se
tion, I see no
ompelling reason programmer who initially
hooses queues is likely to
run into trouble be
ause of the opposite orientations
of the input and output queues. The queuebased algo  jQ 
QQ
1 1 2