about summary refs log blame commit diff stats
path: root/js/games/nluqo.github.io/~bh/ssch19/implement-hof.html
blob: fb954da08110b8a17d5349a9f80f45dadeb4d405 (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
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604



























































































































































































































































































































































































































































































































































































































                                                                                                                                                                                                                                                              
<P>
<A NAME="polly"></A>
<P><P><CENTER><IMG SRC="../ss-pics/polly.jpg" ALT="figure: polly"></CENTER>
<HTML>
<HEAD>
<TITLE>Simply Scheme: Introducing Computer Science ch 19: Implementing Higher-Order Functions</TITLE>
</HEAD>
<BODY>
<HR>
<CITE>Simply Scheme:</CITE>
<CITE>Introducing Computer Science</CITE> 2/e Copyright (C) 1999 MIT
<H2>Chapter 19</H2>
<H1>Implementing Higher-Order Functions</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/ssch19.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="../ssch18/trees.html"><STRONG>BACK</STRONG></A>
chapter thread <A HREF="../ssch20/part6.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>This chapter is about writing <EM><A NAME="g1"></A>higher-order procedures&mdash;</EM>that is, procedures that implement
<A NAME="g2"></A><A NAME="g3"></A>higher-order functions.  We are going to study the implementation
of <CODE>every</CODE>, <CODE>keep</CODE>, and so on.

<P>Really there are no new techniques involved.  You know how to write
recursive procedures that follow the <CODE>every</CODE> pattern, the <CODE>keep</CODE>
pattern, and so on; it's a small additional step to generalize those
<A NAME="g4"></A>
patterns.  The truly important point made in this chapter is that you aren't
limited to a fixed set of higher-order functions.  If you feel a need for a
new one, you can implement it.

<P><H2>Generalizing Patterns</H2>

<P>In Chapter 14, we showed you the procedures <CODE>square-sent</CODE> and <CODE>pigl-sent</CODE>, which follow the <CODE>every</CODE> pattern of recursion.  In order to
write the general tool, <CODE>every</CODE> itself, we have to
<EM>generalize the pattern</EM> that those two have in common.

<P>Before we get to writing higher-order procedures, let's look at a simpler
case of generalizing patterns.

<P>Suppose we want to find out the areas of several different kinds of
shapes, given one linear dimension.  A straightforward way would be to do it
like this:

<P><PRE>(define pi 3.141592654)

(define (<A NAME="g5"></A>square-area r) (* r r))

(define (<A NAME="g6"></A>circle-area r) (* pi r r))

(define (<A NAME="g7"></A>sphere-area r) (* 4 pi r r))

(define (<A NAME="g8"></A>hexagon-area r) (* (sqrt 3) 1.5 r r))

&gt; (square-area 6)
36

&gt; (circle-area 5)
78.53981635
</PRE>

<P>This works fine, but it's somewhat tedious to define all four of
these procedures, given that they're so similar.  Each one returns the square
of its argument times some constant factor; the only difference is the
constant factor.

<P>We want to generalize the pattern that these four procedures
exhibit.  Each of these procedures has a particular constant factor built in
to its definition.  What we'd like instead is one single procedure that lets
you choose a constant factor when you invoke it.  This new procedure will
take a second argument besides the linear dimension <CODE>r</CODE> (the radius or
side): a <CODE>shape</CODE> argument whose value is the desired constant factor.

<P><PRE>(define (<A NAME="g9"></A>area shape r) (* shape r r))
(define square 1)
(define circle pi)
(define sphere (* 4 pi))
(define hexagon (* (sqrt 3) 1.5))

&gt; (area sphere 7)
615.752160184
</PRE>

<P>What's the point?  We started with several procedures.  Then we found that
they had certain points of similarity and certain differences.  In order to
write a single procedure that generalizes the points of similarity, we had to
use an additional argument for each point of difference.  (In this example,
there was only one point of difference.)

<P>In fact, <EM>every</EM> procedure with arguments is a generalization in the
same way.  Even <CODE>square-area</CODE>, which we presented as the special case
to be generalized, is more general than these procedures:

<P><PRE>(define (area-of-square-of-side-5)
  (* 5 5))

(define (area-of-square-of-side-6)
  (* 6 6))
</PRE>

<P>These may seem too trivial to be taken seriously.  Indeed, nobody
would write such procedures.  But it's possible to take the area of a
particular size square without using a procedure at all, and then later
discover that you need to deal with squares of several sizes.

<P>This idea of using a procedure to generalize a pattern is part of the larger
idea of abstraction that we've been discussing throughout the book.  We
notice an algorithm that we need to use repeatedly, and so we separate the
algorithm from any particular data values and give it a name.

<P>The idea of generalization may seem obvious in the example about areas of
squares.  But when we apply the same idea to generalizing over a function,
rather than merely generalizing over a number, we gain the enormous
expressive power of higher-order functions.

<P><H2>The <CODE><B>Every</B></CODE> Pattern Revisited</H2>

<P>Here again is the <CODE>every</CODE> template:

<P><PRE>(define (<B>every-something</B> sent)
  (if (empty? sent)
      '()
      (se (______ (first sent))
	  (<B>every-something</B> (bf sent)))))
</PRE>

<P>You've been writing <CODE>every</CODE>-like procedures by filling in the
blank with a specific function.  To generalize the pattern, we'll use the
trick of adding an argument, as we discussed in the last section.

<P><PRE>(define (<A NAME="g10"></A>every fn sent)
  (if (empty? sent)
      '()
      (se (fn (first sent))
          (every fn (bf sent)))))
</PRE>

<P>This is hardly any work at all for something that seemed as
mysterious as <CODE>every</CODE> probably did when you first saw it.

<P>Recall that <CODE>every</CODE> will also work if you pass it a word as its second
argument.  The version shown here does indeed work for words, because <CODE>first</CODE> and <CODE>butfirst</CODE> work for words.  So probably &quot;<CODE>stuff</CODE>&quot; would
be a better formal parameter than &quot;<CODE>sent</CODE>.&quot;  (The result from <CODE>every</CODE> is always a sentence, because <CODE>sentence</CODE> is used to construct the
result.)

<P>
<P><H2>The Difference between <CODE><B>Map</B></CODE> and <CODE><B>Every</B></CODE></H2>

<P>Here's the definition of the <CODE>map</CODE> procedure:

<P><PRE>(define (<A NAME="g11"></A>map fn lst)
  (if (null? lst)
      '()
      (cons (fn (car lst))
            (map fn (cdr lst)))))
</PRE>

<P>The structure here is identical to that of <CODE>every</CODE>; the only
difference is that we use <CODE>cons</CODE>, <CODE>car</CODE>, and <CODE>cdr</CODE> instead of
<CODE>se</CODE>, <CODE>first</CODE>, and <CODE>butfirst</CODE>.

<P>One implication of this is that you can't use <CODE>map</CODE> with a word, since
it's an error to take the <CODE>car</CODE> of a word.  When is it advantageous
to use <CODE>map</CODE> instead of <CODE>every</CODE>?  Suppose you're using <CODE>map</CODE>
with a structured list, like this:

<P><PRE>&gt; (map (lambda (flavor) (se flavor '(is great)))
       '(ginger (ultra chocolate) pumpkin (rum raisin)))
((GINGER IS GREAT) (ULTRA CHOCOLATE IS GREAT)
 (PUMPKIN IS GREAT) (RUM RAISIN IS GREAT))
</PRE>

<P><PRE>&gt; (every (lambda (flavor) (se flavor '(is great)))
	 '(ginger (ultra chocolate) pumpkin (rum raisin)))
(GINGER IS GREAT ULTRA CHOCOLATE IS GREAT PUMPKIN IS GREAT
 RUM RAISIN IS GREAT)
</PRE>

<P>Why does <CODE>map</CODE> preserve the structure of the sublists while <CODE>every</CODE>
doesn't?  <CODE>Map</CODE> uses <CODE>cons</CODE> to combine the elements of the result,
whereas <CODE>every</CODE> uses <CODE>sentence</CODE>:

<P><PRE>&gt; (cons '(pumpkin is great)
	(cons '(rum raisin is great)
	      '()))
((PUMPKIN IS GREAT) (RUM RAISIN IS GREAT))

&gt; (se '(pumpkin is great)
      (se '(rum raisin is great)
	  '()))
(PUMPKIN IS GREAT RUM RAISIN IS GREAT)
</PRE>

<P><H2><CODE><B>Filter</B></CODE></H2>

<P>Here's the implementation of <A NAME="g12"></A><CODE>filter</CODE>:

<P><PRE>(define (<A NAME="g13"></A>filter pred lst)
  (cond ((null? lst) '())
        ((pred (car lst))
         (cons (car lst) (filter pred (cdr lst))))
        (else (filter pred (cdr lst)))))
</PRE>

<P>Like <CODE>map</CODE>, this uses <CODE>cons</CODE> as the constructor so that it
will work properly on structured lists.  We're leaving the definition of
<CODE>keep</CODE>, the version for words and sentences, as an exercise.

<P>(Aside from the difference between lists and sentences, this is just like
the <CODE>keep</CODE> template on page <A HREF="../ssch14/recur-patterns.html#keeptemplate">there</A>.)

<P><H2><CODE><B>Accumulate</B></CODE> and <CODE><B>Reduce</B></CODE></H2>

<P>Here are the examples of the <A NAME="g14"></A><CODE>accumulate</CODE> pattern that we showed
you before:

<P><PRE>(define (addup nums)
  (if (empty? nums)
      0
      (+ (first nums) (addup (bf nums)))))

(define (scrunch-words sent)
  (if (empty? sent)
      &quot;"
      (word (first sent) (scrunch-words (bf sent)))))
</PRE>

<P>What are the similarities and differences?  There are <EM>two</EM> important
differences between these procedures: the combiners (<CODE>+</CODE> versus <CODE>word</CODE>) and the values returned in the base cases (zero versus the empty
word).  According to what we said about generalizing patterns, you might
expect that we'd need two extra arguments.  You'd invoke <CODE>three-arg-accumulate</CODE> like this:

<P><PRE>&gt; (three-arg-accumulate + 0 '(6 7 8))
21

&gt; (three-arg-accumulate word &quot;&quot; '(come together))
COMETOGETHER
</PRE>

<P>But we've actually defined <CODE>accumulate</CODE> and <A NAME="g15"></A><CODE>reduce</CODE> so
that only two arguments are required, the procedure and the sentence or
list.  We thought it would be too much trouble to have to provide the
identity element all the time.  How did we manage to avoid it?

<P>The trick is that in our <CODE>reduce</CODE> and <CODE>accumulate</CODE> the base case is
a one-element argument, rather than an empty argument.  When we're down to
one element in the argument, we just return that element:

<P><PRE>(define (accumulate combiner stuff)          ;; first version
  (if (empty? (bf stuff))
      (first stuff)
      (combiner (first stuff)
		(accumulate combiner (bf stuff)))))
</PRE>

<P>This version is a simplification of the one we actually provide.
What happens if <CODE>stuff</CODE> is empty?  This version blows up, since it tries
to take the <CODE>butfirst</CODE> of <CODE>stuff</CODE> immediately.  Our final version
has a specific check for empty arguments:

<P><PRE>(define (<A NAME="g16"></A>accumulate combiner stuff)
  (cond ((not (empty? stuff)) (real-accumulate combiner stuff))
        ((member combiner (list + * word se append))
         (combiner))
        (else (error
               &quot;Can't accumulate empty input with that combiner&quot;))))

(define (<A NAME="g17"></A>real-accumulate combiner stuff)
  (if (empty? (bf stuff))
      (first stuff)
      (combiner (first stuff) (real-accumulate combiner (bf stuff)))))
</PRE>

<P>This version works just like the earlier version as long as <CODE>stuff</CODE> isn't empty.  (<CODE>Reduce</CODE> is the same, except that it uses <CODE>null?</CODE>, <CODE>car</CODE>, and <CODE>cdr</CODE>.)

<P>As we mentioned in Chapter 8, many of Scheme's primitive procedures
return their identity element when invoked with no arguments.  We can take
advantage of this; if <CODE>accumulate</CODE> is invoked with an empty second
argument and one of the procedures <CODE>+</CODE>, <CODE>*</CODE>, <CODE>word</CODE>, <CODE>sentence</CODE>, <CODE>append</CODE> or <CODE>list</CODE>, we invoke the combiner with no
arguments to produce the return value.

<P>
<P>On the other hand, if <CODE>accumulate</CODE>'s combiner argument is something like
<CODE>(lambda (x y) (word x '- y))</CODE> or <CODE>max</CODE>, then there's nothing <CODE>accumulate</CODE> can return, so we give an error message.  (But it's a more
descriptive error message than the first version; what message do you get
when you call that first version with an empty second argument?)

<P>It's somewhat of a kludge that we have to include in our procedure a
list of the functions that can be called without arguments.  What we'd like
to do is invoke the combiner and find out if that causes an error, but
Scheme doesn't provide a mechanism for causing errors on purpose and
recovering from them.  (Some dialects of Lisp do have that capability.)

<P><H2>Robustness</H2>

<P>Instead of providing a special error message for empty-argument cases
that <CODE>accumulate</CODE> can't handle, we could have just let it blow up:

<P><PRE>(define (accumulate combiner stuff)          ;; non-robust version
  (if (not (empty? stuff))
      (real-accumulate combiner stuff)
      (combiner)))
</PRE>

<P>Some questions about programming have clear right and wrong answers&mdash;if your
program doesn't work, it's wrong!  But the decision about whether to include
the extra check for a procedure that's usable with an empty argument is a
matter of judgment.

<P>Here is the reasoning in favor of this simpler version:  In either version,
the user who tries to evaluate an expression like

<P><PRE>(accumulate max '())
</PRE>

<P>is going to get an error message.  In the longer version we've
spent both our own programming effort and a little of the computer's time
on every invocation just to give a <EM>different</EM> error message from the
one that Scheme would have given anyway.  What's the point?

<P>Here is the reasoning in favor of the longer version:  In practice, the
empty-argument situation isn't going to arise because someone uses a quoted
empty sentence; instead the second argument to <CODE>accumulate</CODE> will be some
expression whose value happens to be empty under certain conditions.  The
user will then have to debug the program that caused those conditions.
Debugging is hard; we should make it easier for the user, if we can, by
giving an error message that points clearly to the problem.

<P>A program that behaves politely when given incorrect input is called <EM>robust.</EM> It's not always a matter of better or worse error
messages.  For example, a program that reads input from a human user might
offer the chance to try again if some input value is incorrect.  A robust
program will also be alert for hardware problems, such as running out of
space on a disk, or getting garbled information over a telephone connection
to another machine because of noise on the line.

<P>It's possible to pay either too little or too much attention to program
robustness.  If you're a professional programmer, your employer will expect
your programs to survive errors that are likely to happen.  On the other
hand, your programs will be hard to read and debug if the error checking
swamps the real work!  As a student, unless you are specifically asked to
&quot;bulletproof&quot; your program, don't answer exam questions by writing
procedures like this one:

<P><PRE>(define (even? num)                          ;; silly example
  (cond ((not (number? num)) (error &quot;Not a number.&quot;))
        ((not (integer? num)) (error &quot;Not an integer.&quot;))
        ((&lt; num 0) (error &quot;Argument must be positive.&quot;))
        (else (= (remainder num 2) 0))))
</PRE>

<P>In the case of <CODE>accumulate</CODE>, we decided to be extra robust
because we were writing a procedure for use in a beginning programming
course.  If we were writing this tool just for our own use, we might have
chosen the non-robust version.  Deciding how robust a program will be is a
matter of taste.

<P><H2>Higher-Order Functions for Structured Lists</H2>

<P>We've given you a fairly standard set of higher-order functions, but there's
no law that says these are the only ones.  Any time you notice yourself
writing what feels like the same procedure over again, but with different
details, consider inventing a higher-order function.

<P>For example, here's a procedure we defined in Chapter 17.

<P><PRE>(define (<A NAME="g18"></A>deep-pigl structure)
  (cond ((word? structure) (pigl structure))
	((null? structure) '())
	(else (cons (deep-pigl (car structure))
		    (deep-pigl (cdr structure))))))
</PRE>

<P>This procedure converts every word in a <A NAME="g19"></A><A NAME="g20"></A>structured list
to Pig Latin.
Suppose we have a structure full of numbers and we want to compute all of their
squares.  We could write a specific procedure <CODE>deep-square</CODE>, but
instead, we'll write a higher-order procedure:

<P><PRE>(define (<A NAME="g21"></A>deep-map f structure)
  (cond ((word? structure) (f structure))
        ((null? structure) '())
        (else (cons (deep-map f (car structure))
                    (deep-map f (cdr structure))))))
</PRE>

<P><H2>The Zero-Trip Do Loop</H2>

<P>The first programming language that provided a level of abstraction over the
instructions understood directly by computer hardware was Fortran, a
language that is still widely used today despite the advances in programming
language design since then.  Fortran remains popular because of the enormous
number of useful programs that have already been written in it; if an
improvement is needed, it's easier to modify the Fortran program than to
start again in some more modern language.

<P>Fortran includes a control mechanism called <CODE>do</CODE>, a sort of higher-order
procedure that carries out a computation repeatedly, as <CODE>every</CODE> does.
But instead of carrying out the computation once for each element of a given
collection of data (like the sentence argument to <CODE>every</CODE>), <CODE>do</CODE>
performs a computation once for each integer in a range specified by its
endpoints.  &quot;For every number between 4 and 16, do such-and-such.&quot;

<P>What if you specify endpoints such that the starting value is greater than
the ending value?  In the first implementation of Fortran, nobody thought
very hard about this question, and they happened to implement <CODE>do</CODE> in
such a way that if you specified a backward range, the computation was done
once, for the given starting value, before Fortran noticed that it was past
the ending value.

<P>Twenty years later, a bunch of computer scientists argued that this behavior
was wrong&mdash;that a <CODE>do</CODE> loop with its starting value greater than its
ending value should not carry out its computation at all.  This proposal for
a &quot;zero-trip <CODE>do</CODE> loop&quot; was strongly opposed by Fortran old-timers,
not because of any principle but because of all the thousands of Fortran
programs that had been written to rely on the one-trip behavior.

<P>The point of this story is that the Fortran users had to debate the issue so
heatedly because they are stuck with only the control mechanisms that are
built into the language.  Fortran doesn't have the idea of function as data,
so Fortran programmers can't write their own higher-order procedures.  But
you, using the techniques of this chapter, can create precisely the control
mechanism that you need for whatever problem you happen to be working on.

<P><H2>Pitfalls</H2>

<P>The most crucial point in inventing a higher-order function is to make
sure that the pattern you have in mind really does generalize.  For example,
if you want to write a higher-order function for structured data, what is the base
case?  Will you use the tree abstract data type, or will you use <CODE>car</CODE>/<CODE>cdr</CODE>
recursion?

<P>When you generalize a pattern by adding a new argument (typically a
procedure), be sure you add it to the recursive invocation(s) as well as to
the formal parameter list!

<P><H2>Boring Exercises</H2>

<P><B>19.1</B>&nbsp;&nbsp;What happens if you say the following?

<P><PRE>(every cdr '((john lennon) (paul mccartney)
	     (george harrison) (ringo starr)))
</PRE>

<P>How is this different from using <CODE>map</CODE>, and why?  How about <CODE>cadr</CODE> instead of <CODE>cdr</CODE>?


<P>
<H2>Real Exercises</H2>

<P><B>19.2</B>&nbsp;&nbsp;Write <CODE>keep</CODE>.  Don't forget that <CODE>keep</CODE> has to return a sentence if
its second argument is a sentence, and a word if its second argument is a
word.

<P>(Hint: it might be useful to write a <CODE>combine</CODE> procedure that uses either
<CODE>word</CODE> or <CODE>sentence</CODE> depending on the types of its arguments.)


<P>
<B>19.3</B>&nbsp;&nbsp;Write the three-argument version of <CODE>accumulate</CODE> that we described.

<P><PRE>&gt; (three-arg-accumulate + 0 '(4 5 6))
15

&gt; (three-arg-accumulate + 0 '())
0

&gt; (three-arg-accumulate cons '() '(a b c d e))
(A B C D E)
</PRE>

<P>
<B>19.4</B>&nbsp;&nbsp;Our <CODE>accumulate</CODE> combines elements from right to left.  That is,

<P><PRE>(accumulate - '(2 3 4 5))
</PRE>

<P>computes 2&minus;(3&minus;(4&minus;5)).  Write <CODE>left-accumulate</CODE>, which
will compute ((2&minus;3)&minus;4)&minus;5 instead.  (The result will be the same for
an operation such as <CODE>+</CODE>, for which grouping order doesn't matter, but
will be different for <CODE>-</CODE>.)


<P>
<B>19.5</B>&nbsp;&nbsp;Rewrite the <CODE>true-for-all?</CODE> procedure from Exercise <A HREF="../ssch8/higher.html#trueforall">8.10</A>.
Do not use <CODE>every</CODE>, <CODE>keep</CODE>, or <CODE>accumulate</CODE>.


<P>
<B>19.6</B>&nbsp;&nbsp;Write a procedure <CODE><A NAME="g22"></A>true-for-any-pair?</CODE> that takes a predicate and a
sentence as arguments.  The predicate must accept two words as its arguments.
Your procedure should return <CODE>#t</CODE> if the argument predicate will return
true for any two adjacent words in the sentence:
<A NAME="exanypair"></A>

<P><PRE>&gt; (true-for-any-pair? equal? '(a b c b a))
#F

&gt; (true-for-any-pair? equal? '(a b c c d))
#T

&gt; (true-for-any-pair? &lt; '(20 16 5 8 6))      ;; 5 is less than 8
#T
</PRE>

<P>
<B>19.7</B>&nbsp;&nbsp;Write a procedure <CODE><A NAME="g23"></A>true-for-all-pairs?</CODE> that takes a predicate
and a sentence as arguments.  The predicate must accept two words as its
arguments.  Your procedure should return <CODE>#t</CODE> if the argument predicate
will return true for <EM>every</EM> two adjacent words in the sentence:
<A NAME="exallpairs"></A>

<P><PRE>&gt; (true-for-all-pairs? equal? '(a b c c d))
#F

&gt; (true-for-all-pairs? equal? '(a a a a a))
#T

&gt; (true-for-all-pairs? &lt; '(20 16 5 8 6))
#F

&gt; (true-for-all-pairs? &lt; '(3 7 19 22 43))
#T
</PRE>

<P>
<B>19.8</B>&nbsp;&nbsp;Rewrite <CODE>true-for-all-pairs?</CODE> (Exercise <A HREF="implement-hof.html#exallpairs">19.7</A>) using <CODE>true-for-any-pair?</CODE> (Exercise <A HREF="implement-hof.html#exanypair">19.6</A>) as a helper procedure.  Don't use
recursion in solving this problem (except for the recursion you've already
used to write <CODE>true-for-any-pair?</CODE>).  Hint:  You'll find the <CODE>not</CODE>
procedure helpful.


<P>
<B>19.9</B>&nbsp;&nbsp;Rewrite either of the sort procedures from Chapter 15 to take two
arguments, a list and a predicate.  It should sort the elements of that list
according to the given predicate:

<P><PRE>&gt; (sort '(4 23 7 5 16 3) &lt;)
(3 4 5 7 16 23)

&gt; (sort '(4 23 7 5 16 3) &gt;)
(23 16 7 5 4 3)

&gt; (sort '(john paul george ringo) before?)
(GEORGE JOHN PAUL RINGO)
</PRE>

<P>
<B>19.10</B>&nbsp;&nbsp;Write <CODE>tree-map</CODE>, analogous to our <CODE>deep-map</CODE>, but for trees, using
the <CODE>datum</CODE> and <CODE>children</CODE> selectors.


<P>
<B>19.11</B>&nbsp;&nbsp;Write <CODE>repeated</CODE>.  (This is a hard exercise!)


<P>
<B>19.12</B>&nbsp;&nbsp;Write <CODE>tree-reduce</CODE>.  You may assume that the combiner argument can
be invoked with no arguments.

<P><PRE>&gt; (tree-reduce
   +
   (make-node 3 (list (make-node 4 '())
		      (make-node 7 '())
		      (make-node 2 (list (make-node 3 '())
					 (make-node 8 '()))))))
27
</PRE>

<P>
<B>19.13</B>&nbsp;&nbsp;Write <CODE>deep-reduce</CODE>, similar to <CODE>tree-reduce</CODE>, but for structured
lists:

<P><PRE>&gt; (deep-reduce word '(r ((a (m b) (l)) (e (r)))))
RAMBLER
</PRE>


<P>
<HR>
<P><A HREF="../ss-toc2.html">(back to Table of Contents)</A><P>
<A HREF="../ssch18/trees.html"><STRONG>BACK</STRONG></A>
chapter thread <A HREF="../ssch20/part6.html"><STRONG>NEXT</STRONG></A>

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