about summary refs log blame commit diff stats
path: root/js/games/nluqo.github.io/~bh/ssch15/adv-recur
blob: d6444e305ae1ebf3e3482cea36e52d4df28aefbd (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553








































































































































































































































































































































































































































































































































































                                                                                                                                                                                                                
<P>
<A NAME="fractal"></A>
<P><CENTER><IMG SRC="../ss-pics/fractal.jpg" ALT="figure: fractal"></CENTER><P><CENTER>Zoom in on some parts of a fractal and you'll see a miniature version
of the whole thing.
</CENTER><P>

<HTML>
<HEAD>
<TITLE>Simply Scheme: Introducing Computer Science ch 15: Advanced Recursion</TITLE>
</HEAD>
<BODY>
<HR>
<CITE>Simply Scheme:</CITE>
<CITE>Introducing Computer Science</CITE> 2/e Copyright (C) 1999 MIT
<H2>Chapter 15</H2>
<H1>Advanced Recursion</H1>

<TABLE width="100%"><TR><TD>
<IMG SRC="../simply.jpg" ALT="cover photo">
<TD><TABLE>
<TR><TD align="right"><CITE><A HREF="http://www.cs.berkeley.edu/~bh/">Brian
Harvey</A><BR>University of California, Berkeley</CITE>
<TR><TD align="right"><CITE><A HREF="http://ccrma.stanford.edu/~matt">Matthew
Wright</A><BR>University of California, Santa Barbara</CITE>
<TR><TD align="right"><BR>
<TR><TD align="right"><A HREF="../pdf/ssch15.pdf">Download PDF version</A>
<TR><TD align="right"><A HREF="../ss-toc2.html">Back to Table of Contents</A>
<TR><TD align="right"><A HREF="../ssch14/number-name.html"><STRONG>BACK</STRONG></A>
chapter thread <A HREF="poker.html"><STRONG>NEXT</STRONG></A>
<TR><TD align="right"><A HREF="http://mitpress.mit.edu/0262082810">MIT
Press web page for <CITE>Simply Scheme</CITE></A>
</TABLE></TABLE>

<HR>


<P>By now you've had a good deal of experience with straightforward recursive
problems, and we hope you feel comfortable with them.  In this chapter, we
present some more challenging problems.  But the same leap of faith method
that we used for easier problems is still our basic approach.

<P><H2>Example: <CODE>Sort</CODE></H2>

<P>First we'll consider the example of sorting a sentence.  The argument
will be any sentence; our procedure will return a sentence with the same
words in alphabetical order.

<P><PRE>&gt; (sort '(i wanna be your man))
(BE I MAN WANNA YOUR)
</PRE>

<P>We'll use the <CODE>before?</CODE> primitive to decide if one word comes before
another word alphabetically:

<P><PRE>&gt; (before? 'starr 'best)
#F
</PRE>

<P>How are we going to think about this problem recursively?  Suppose that
we're given a sentence to sort.  A relatively easy subproblem is to find the
word that ought to come first in the sorted sentence; we'll write <CODE>earliest-word</CODE> later to do this.

<P>Once we've found that word, we just need to put it in front of the sorted
version of the rest of the sentence.  This is our leap of faith:
We're going to assume that we can already sort this smaller sentence.
The algorithm we've described is called <EM>selection</EM> sort.

<P>
Another subproblem is to find the &quot;rest of the sentence&quot;&mdash;all the words
except for the earliest.  But in Exercise <A HREF="../ssch14/recur-patterns.html#remonce">14.1</A> you wrote a function
<CODE>remove-once</CODE> that takes a word and a sentence and returns the sentence
with that word removed.  (We don't want to use <CODE>remove</CODE>, which removes
all copies of the word, because our argument sentence might include the same
word twice.)

<P>Let's say in Scheme what we've figured out so far:

<P><PRE>(define (sort sent)                          ;; unfinished
  (se (earliest-word sent)
      (sort (remove-once (earliest-word sent) sent))))
</PRE>

<P>We need to add a base case.  The smallest sentence is <CODE>()</CODE>,
which is already sorted.

<P><PRE>(define (<A NAME="g1"></A>sort sent)
  (if (empty? sent)
      '()
      (se (earliest-word sent)
	  (sort (remove-once (earliest-word sent) sent)))))
</PRE>

<P>We have one unfinished task: finding the earliest word of the argument.

<P><PRE>(define (<A NAME="g2"></A>earliest-word sent)
  (earliest-helper (first sent) (bf sent)))

(define (earliest-helper so-far rest)
  (cond ((empty? rest) so-far)
	((before? so-far (first rest))
	 (earliest-helper so-far (bf rest)))
	(else (earliest-helper (first rest) (bf rest)))))<A NAME="text1" HREF="adv-recur#ft1">[1]</A></PRE>

<P>

<P>For your convenience, here's <CODE>remove-once</CODE>:
<PRE>(define (<A NAME="g3"></A>remove-once wd sent)
  (cond ((empty? sent) '())
	((equal? wd (first sent)) (bf sent))
	(else (se (first sent) (remove-once wd (bf sent))))))
</PRE>

<P> 
<H2>Example: <CODE>From-Binary</CODE></H2>

<P>

<P>We want to take a word of ones and zeros, representing a
<A NAME="g4"></A><A NAME="g5"></A>binary number, and compute the numeric value that it represents.
Each binary digit (or <EM>bit</EM>) corresponds to a power of two, just as
ordinary decimal digits represent powers of ten.  So the binary number 1101
represents (1&times;8)+(1&times;4)+(0&times;2)+(1&times;1)&nbsp;=&nbsp;13.  We want to be able to say

<P><PRE>&gt; (from-binary 1101)
13

&gt; (from-binary 111)
7
</PRE>


<P>Where is the smaller, similar subproblem?  Probably the most obvious thing
to try is our usual trick of dividing the argument into its <CODE>first</CODE> and
its <CODE>butfirst</CODE>.  Suppose we divide the binary number <CODE>1101</CODE> that
way.  We make the leap of faith by assuming that we can translate the
butfirst, <CODE>101</CODE>, into its binary value 5.  What do we have to add for
the leftmost <CODE>1</CODE>?  It contributes 8 to the total, because it's three
bits away from the right end of the number, so it must be multiplied by
2<SUP><SMALL>3</SMALL></SUP>.  We could write this idea as follows:

<P><PRE>(define (from-binary bits)                   ;; incomplete 
  (+ (* (first bits) (expt 2 (count (bf bits))))
     (from-binary (bf bits))))
</PRE>

<P>That is, we multiply the <CODE>first</CODE> bit by a power of two
depending on the number of bits remaining, then we add that
to the result of the recursive call.

<P>As usual, we have written the algorithm for the recursive case before
figuring out the base case.  But it's pretty easy; a number with no bits (an
empty word) has the value zero.<A NAME="text2" HREF="adv-recur#ft2">[2]</A>

<P><PRE>(define (<A NAME="g6"></A>from-binary bits)
  (if (empty? bits)
      0
      (+ (* (first bits) (expt 2 (count (bf bits))))
         (from-binary (bf bits)))))
</PRE>

<P>Although this procedure is correct, it's worth noting that a more efficient
version can be written by dissecting the number from right to left.  As
you'll see, we can then avoid the calls to <CODE>expt</CODE>, which are expensive
because we have to do more multiplication than should be necessary.

<P>Suppose we want to find the value of the binary number <CODE>1101</CODE>.  The <CODE>butlast</CODE> of this number, <CODE>110</CODE>, has the value six.  To get the value of
the entire number, we double the six (because <CODE>1100</CODE> would have the value
12, just as in ordinary decimal numbers 430 is ten times 43) and then add the
rightmost bit to get 13.  Here's the new version:

<P><PRE>(define (<A NAME="g7"></A>from-binary bits)
  (if (empty? bits)
      0
      (+ (* (from-binary (bl bits)) 2)
         (last bits))))
</PRE>

<P>This version may look a little unusual.  We usually combine the value
returned by the recursive call with some function of the current element.
This time, we are combining the current element itself with a function of
the recursive return value.  You may want to trace this procedure to see how
the intermediate return values contribute to the final result.

<P><H2>Example: <CODE>Mergesort</CODE></H2>

<P>Let's go back to the problem of sorting a sentence.  It turns out that
sorting one element at a time, as in selection sort, isn't the fastest
possible approach.  One of the fastest sorting algorithms is called <EM>mergesort,</EM> and it works like this: In order to mergesort a
sentence, divide the sentence into two equal halves and recursively sort
each half.  Then take the two sorted subsentences and <EM>merge</EM> them
together, that is, create one long sorted sentence that contains all the words
of the two halves.  The base case is that an empty sentence or a one-word
sentence is already sorted.

<P><PRE>(define (<A NAME="g8"></A>mergesort sent)
  (if (&lt;= (count sent) 1)
      sent
      (merge (mergesort (one-half sent))
             (mergesort (other-half sent)))))
</PRE>

<P>The leap of faith here is the idea that we can magically <CODE>mergesort</CODE>
the halves of the sentence.  If you try to trace this through step by step,
or wonder exactly what happens at what time, then this algorithm may be very
confusing.  But if you just <EM>believe</EM> that the recursive calls will do
exactly the right thing, then it's much easier to understand this program.
The key point is that if the two smaller pieces have already been sorted,
it's pretty easy to merge them while keeping the result in order.

<P>We still need some helper procedures.  You wrote <CODE>merge</CODE> in Exercise
<A HREF="../ssch14/recur-patterns.html#mergeex">14.15</A>.  It uses the following technique: Compare the first words of the
two sentences.  Let's say the first word of the sentence on the left is
smaller.  Then the first word of the return value is the first word of the
sentence on the left.  The rest of the return value comes from recursively
merging the <CODE>butfirst</CODE> of the left sentence with the entire right
sentence.  (It's precisely the opposite of this if the first word of the
other sentence is smaller.)  

<P><PRE>(define (<A NAME="g9"></A>merge left right)
  (cond ((empty? left) right)
	((empty? right) left)
	((before? (first left) (first right))
	 (se (first left) (merge (bf left) right)))
	(else (se (first right) (merge left (bf right))))))
</PRE>

<P>Now we have to write <CODE>one-half</CODE> and <CODE>other-half</CODE>.  One of the
easiest ways to do this is to have <CODE>one-half</CODE> return the elements in
odd-numbered positions, and have <CODE>other-half</CODE> return the elements in
even-numbered positions.  These are the same as the procedures <CODE>odds</CODE>
(from Exercise <A HREF="../ssch14/recur-patterns.html#exodds">14.4</A>) and <CODE>evens</CODE> (from Chapter 12).

<P><PRE>(define (<A NAME="g10"></A>one-half sent)
  (if (&lt;= (count sent) 1)
      sent
      (se (first sent) (one-half (bf (bf sent))))))

(define (<A NAME="g11"></A>other-half sent)
  (if (&lt;= (count sent) 1)
      '()
      (se (first (bf sent)) (other-half (bf (bf sent))))))
</PRE>

<P><H2>Example: <CODE>Subsets</CODE></H2>

<P>We're now going to attack a much harder problem.  We want to know all the
subsets of the letters of a word&mdash;that is, words that can be formed
from the original word by crossing out some (maybe zero) of the letters.  For
example, if we start with a short word like <CODE>rat</CODE>, the subsets are <CODE>r</CODE>, <CODE>a</CODE>, <CODE>t</CODE>, <CODE>ra</CODE>, <CODE>rt</CODE>, <CODE>at</CODE>, <CODE>rat</CODE>, and the empty
word (<CODE>&quot;&quot;</CODE>).  As the word gets longer, the number of subsets gets bigger
very quickly.<A NAME="text3" HREF="adv-recur#ft3">[3]</A>

<P>As with many problems about words, we'll try assuming that we can find the
subsets of the <CODE>butfirst</CODE> of our word.  In other words, we're hoping to
find a solution that will include an expression like

<P><PRE>(subsets (bf wd))
</PRE>

<P>Let's actually take a four-letter word and look at its subsets.  We'll pick
<CODE>brat</CODE>, because we already know the subsets of its <CODE>butfirst</CODE>.  Here
are the subsets of <CODE>brat</CODE>:

<P><PRE>&quot;&quot; b r a t br ba bt ra rt at bra brt bat rat brat
</PRE>

<P>You might notice that many of these subsets are also subsets of <CODE>rat</CODE>.
In fact, if you think about it, <EM>all</EM> of the subsets of <CODE>rat</CODE> are
also subsets of <CODE>brat</CODE>.  So the words in <CODE>(subsets 'rat)</CODE> are some
of the words we need for <CODE>(subsets 'brat)</CODE>.

<P>Let's separate those out and look at the ones left over:

<P><TABLE><TR><TD><CODE>rat</CODE> subsets:
<TD>&nbsp;&nbsp;<CODE>""<TD>&nbsp;<CODE>r</CODE>
<TD>&nbsp;<CODE>a</CODE><TD>&nbsp;<CODE>t</CODE>
<TD>&nbsp;<CODE>ra</CODE><TD>&nbsp;<CODE>rt</CODE>
<TD>&nbsp;<CODE>at</CODE><TD>&nbsp;<CODE></CODE><TD>&nbsp;<CODE>rat</CODE>
<TR><TD>others:
<TD>&nbsp;&nbsp;<CODE>b</CODE><TD>&nbsp;<CODE>br</CODE>
<TD>&nbsp;<CODE>ba</CODE><TD>&nbsp;<CODE>bt</CODE>
<TD>&nbsp;<CODE>bra</CODE><TD>&nbsp;<CODE>brt</CODE>
<TD>&nbsp;<CODE>bat</CODE><TD>&nbsp;<CODE></CODE><TD>&nbsp;<CODE>brat</CODE>
</TABLE>


<P><P>
Right about now you're probably thinking, &quot;They've pulled a rabbit out of a
hat, the way my math teacher always does.&quot; The words that aren't subsets of
<CODE>rat</CODE> all start with <CODE>b</CODE>, followed by something that <EM>is</EM> a
subset of <CODE>rat</CODE>.  You may be thinking that you never would have thought
of that yourself.  But we're just following the method:  Look at the smaller
case and see how it fits into the original problem.  It's not so different
from what happened with <CODE>downup</CODE>.

<P>Now all we have to do is figure out how to say in Scheme, &quot;Put a <CODE>b</CODE>
in front of every word in this sentence.&quot;  This is a straightforward
example of the <CODE>every</CODE> pattern:

<P><PRE>(define (<A NAME="g12"></A>prepend-every letter sent)
  (if (empty? sent)
      '()
      (se (word letter (first sent))
	  (prepend-every letter (bf sent)))))
</PRE>

<P>The way we'll use this in <CODE>(subsets 'brat)</CODE> is

<P><PRE>(prepend-every 'b (subsets 'rat))
</PRE>

<P>Of course in the general case we won't have <CODE>b</CODE> and <CODE>rat</CODE> in our
program, but instead will refer to the formal parameter:

<P><PRE>(define (subsets wd)                         ;; first version
  (se (subsets (bf wd))
      (prepend-every (first wd) (subsets (bf wd)))))
</PRE>

<P>We still need a base case.  By now you're accustomed to the idea of using an
empty word as the base case.  It may be strange to think of the empty word
as a set in the first place, let alone to try to find its subsets.  But a
set of zero elements is a perfectly good set, and it's the smallest one
possible.

<P>The empty set has only one subset, the empty set itself.  What should
<CODE>subsets</CODE> of the empty word return?  It's easy to make a mistake here
and return the empty word itself.  But we want <CODE>subsets</CODE> to return a
sentence, containing all the subsets, and we should stick with returning a
sentence even in the simple case.<A NAME="text4" HREF="adv-recur#ft4">[4]</A> (This mistake would come from not thinking about
the <EM>range</EM> of our function, which is sentences.  This is why we put
so much effort into learning about domains and ranges in Chapter
2.)  So we'll return a sentence containing one (empty) word to
represent the one subset.

<P><PRE>(define (subsets wd)                         ;; second version
  (if (empty? wd)
      (se &quot;&quot;)
      (se (subsets (bf wd))
          (prepend-every (first wd) (subsets (bf wd))))))
</PRE>

<P>This program is entirely correct.  Because it uses two identical recursive
calls, however, it's a lot slower than necessary.  We can use <CODE>let</CODE> to
do the recursive subproblem only once:<A NAME="text5" HREF="adv-recur#ft5">[5]</A>

<P><PRE>(define (<A NAME="g13"></A>subsets wd)
  (if (empty? wd)
      (se &quot;&quot;)
      (let ((smaller (subsets (bf wd))))
        (se smaller
            (prepend-every (first wd) smaller)))))
</PRE>

<P><H2>Pitfalls</H2>

<P>We've already mentioned the need to be careful about the value returned
in the base case.  The <CODE>subsets</CODE> procedure is particularly error-prone
because the correct value, a sentence containing the empty word, is quite
unusual.  An empty subset isn't the same as no subsets at all!

<P>Sometimes you write a recursive procedure with a correct recursive case
and a reasonable base case, but the program still doesn't work.  The trouble
may be that the base case doesn't quite catch all of the ways in which the
problem can get smaller.  A second base case may be needed.  For example, in
<CODE>mergesort</CODE>, why did we write the following line?

<P><PRE>(&lt;= (count sent) 1)
</PRE>

<P>This tests for two base cases, empty sentences and one-word
sentences, whereas in most other examples the base case is just an empty
sentence.  Suppose the base case test were <CODE>(empty? sent)</CODE> and suppose we
invoke <CODE>mergesort</CODE> with a one-word sentence, <CODE>(test)</CODE>.  We would end
up trying to compute the expression

<P><PRE>(merge (mergesort (one-half '(test)))
       (mergesort (other-half '(test))))
</PRE>

<P>If you look back at the definitions of <CODE>one-half</CODE> and
<CODE>other-half</CODE>, you'll see that this is equivalent to

<P><PRE>(merge (mergesort '(test)) (mergesort '()))
</PRE>

<P>The first argument to <CODE>merge</CODE> is the same expression we
started with!  Here is a situation in which the problem doesn't get smaller
in a recursive call.  Although we've been trying to avoid complicated base
cases, in this situation a straightforward base case isn't enough.  To avoid
an infinite recursion, we must have two base cases.

<P>Another example is the <CODE>fib</CODE> procedure from Chapter .  Suppose
it were defined like this:

<P><PRE>(define (fib n)                              ;; wrong!
  (if (= n 1)
      1
      (+ (fib (- n 1))
         (fib (- n 2)))))
</PRE>

<P>It would be easy to make this mistake, because everybody knows
that in a recursion dealing with numbers, the base case is the smallest
possible number.  But in <CODE>fib</CODE>, each computation depends on <EM>two</EM>
smaller values, and we discover that we need two base cases.

<P>The technique of recursion is often used to do something repetitively,
but don't get the idea that the word &quot;recursion&quot; <EM>means</EM>
repetition.  Recursion is a technique in which a procedure invokes itself.
We do use recursion to solve repetitive problems, but don't confuse the
method with the ends it achieves.  In particular, if you've programmed in
other languages that have special-purpose looping mechanisms (the ones
with names like <CODE>for</CODE> and <CODE>while</CODE>), those aren't recursive.
Conversely, not every recursive procedure carries out a repetition.

<P> 
<H2>Exercises</H2>

<P><B>15.1</B>&nbsp;&nbsp;Write a procedure <CODE>to-binary</CODE>:

<P><PRE>&gt; (to-binary 9)
1001

&gt; (to-binary 23)
10111
</PRE>

<P>
<B>15.2</B>&nbsp;&nbsp;A &quot;palindrome&quot; is a sentence that reads the same backward as forward.
Write a predicate <CODE>palindrome?</CODE> that takes a sentence as argument and
decides whether it is a palindrome.  For example:

<P><PRE>&gt; (palindrome? '(flee to me remote elf))
#T

&gt; (palindrome? '(flee to me remote control))
#F
</PRE>

<P>Do not reverse any words or sentences in your solution.


<P>
<B>15.3</B>&nbsp;&nbsp;Write a procedure <CODE><A NAME="g14"></A>substrings</CODE> that takes a word as its
argument.  It should return a sentence containing all of the substrings of
the argument.  A <EM>substring</EM> is a subset whose letters come
consecutively in the original word.  For example, the word <CODE>bat</CODE> is a
subset, but <EM>not</EM> a substring, of <CODE>brat</CODE>.
<A NAME="substrings"></A>


<P>
<B>15.4</B>&nbsp;&nbsp;Write a predicate procedure <CODE><A NAME="g15"></A>substring?</CODE> that takes two words as
arguments and returns <CODE>#t</CODE> if and only if the first word is a substring
of the second.  (See Exercise <A HREF="adv-recur.html#substrings">15.3</A> for the definition of a
substring.)

<P>Be careful about cases in which you encounter a &quot;false start,&quot; like this:

<P><PRE>&gt; (substring? 'ssip 'mississippi)
#T
</PRE>

<P>and also about subsets that don't appear as consecutive letters
in the second word:

<P><PRE>&gt; (substring? 'misip 'mississippi)
#F
</PRE>

<P>
<B>15.5</B>&nbsp;&nbsp;Suppose you have a phone number, such as 223-5766, and you'd like to figure out
a clever way to spell it in letters for your friends to remember.  Each
digit corresponds to three possible letters.  For example, the digit 2
corresponds to the letters A, B, and C.  Write a procedure that takes a
number as argument and returns a sentence of all the possible spellings:

<P><PRE>&gt; (<A NAME="g16"></A>phone-spell 2235766)
(AADJPMM AADJPMN &hellip;CCFLSOO)
</PRE>

<P>(We're not showing you all 2187 words in this sentence.)  You may
assume there are no zeros or ones in the number, since those don't have
letters.

<P>Hint:  This problem has a lot in common with the subsets example.


<P>
<B>15.6</B>&nbsp;&nbsp;Let's say a gladiator kills a roach.  If we want to talk about the roach, we
say &quot;the roach the gladiator killed.&quot; But if we want to talk about the
gladiator, we say &quot;the gladiator that killed the roach.&quot;

<P>People are pretty good at understanding even rather long sentences
as long as they're straightforward: &quot;This is the farmer who kept
the cock that waked the priest that married the man that kissed the
maiden that milked the cow that tossed the dog that worried the cat
that killed the rat that ate the malt that lay in the house that
Jack built.&quot; But even a short <EM>nested</EM> sentence is
confusing:  &quot;This is the rat the cat the dog worried killed.&quot;
Which rat was that?

<P>Write a procedure <CODE><A NAME="g17"></A>unscramble</CODE> that takes a nested
sentence as argument and returns a straightforward sentence about
the same cast of characters:

<P><PRE>&gt; (unscramble '(this is the roach the gladiator killed))
(THIS IS THE GLADIATOR THAT KILLED THE ROACH)

&gt; (unscramble '(this is the rat the cat the dog the boy the
                     girl saw owned chased bit))
(THIS IS THE GIRL THAT SAW THE BOY THAT OWNED THE DOG THAT
      CHASED THE CAT THAT BIT THE RAT)
</PRE>

<P>You may assume that the argument has exactly the structure
of these examples, with no special cases like &quot;that lay <EM>in</EM> the
house&quot; or &quot;that <EM>Jack</EM> built.&quot;


<P>

<HR>
<A NAME="ft1" HREF="adv-recur#text1">[1]</A> If you've read Part III, you might instead want to use <CODE>accumulate</CODE> for this purpose:

<P><PRE>(define earliest-word sent)
  (accumulate (lambda (wd1 wd2) (if (before? wd1 wd2) wd1 wd2))
	      sent))
</PRE><P>
<A NAME="ft2" HREF="adv-recur#text2">[2]</A> A more straightforward base case
would be a one-bit number, but we've reduced that to this more elegant base
case, following the principle we discussed on page <A HREF="../ssch12/leap.html#basecasereduce">there</A>.<P>
<A NAME="ft3" HREF="adv-recur#text3">[3]</A> Try writing down all the subsets of a five-letter word
if you don't believe us.<P>
<A NAME="ft4" HREF="adv-recur#text4">[4]</A> We discussed this point in a
pitfall in Chapter 12.<P>
<A NAME="ft5" HREF="adv-recur#text5">[5]</A> How come we're worrying about
efficiency all of a sudden?  We really <EM>did</EM> pull this out of a hat.
The thing is, it's a <EM>lot</EM> slower without the <CODE>let</CODE>.  Adding one
letter to the length of a word doubles the time required to find its
subsets; adding 10 letters multiplies the time by about 1000.<P>
<P><A HREF="../ss-toc2.html">(back to Table of Contents)</A><P>
<A HREF="../ssch14/number-name.html"><STRONG>BACK</STRONG></A>
chapter thread <A HREF="poker.html"><STRONG>NEXT</STRONG></A>

<P>
<ADDRESS>
<A HREF="../index.html">Brian Harvey</A>, 
<CODE>bh@cs.berkeley.edu</CODE>
</ADDRESS>
</BODY>
</HTML>