about summary refs log tree commit diff stats
path: root/js/games/nluqo.github.io/~bh/61a-pages/Solutions/week2
blob: 6cd29994963a424a003227977651d5c07f45944e (plain) (blame)
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
CS 61A		Week 2		Lab and Homework Solutions

FIRST LAB:

Problem 1:

f	Any definition at all will do:
		(define f 'hello)		f is  hello
		(define f (+ 2 3))		f is  5
		(define (f x) (+ x 7))		f is  #<procedure f>

(f)	This expression says to invoke f as a procedure with no
	arguments.  For that to work, we must DEFINE f as a procedure
	with no arguments:
		(define (f) 'hello)		(f) is  hello
		(define (f) (+ 2 3))		(f) is  5
	Each of these is shorthand for an explicit use of lambda:
		(define f (lambda () 'hello))
		(define f (lambda () (+ 2 3))

(f 3)	This expression says to invoke f as a procedure with an
	argument, so we have to define it that way:
		(define (f x) (+ x 5))		(f 3) is  8
		(define (f x) 'hello)		(f 3) is  hello
		(define (f x) (word x x))	(f 3) is  33
	Again, these definitions are shorthand for lambda expressions:
		(define f (lambda (x) (+ x 5)))
		(define f (lambda (x) 'hello))
		(define f (lambda (x) (word x x)))

((f))	This expression says, first of all, to compute the subexpression
	(f), which invokes f as a procedure with no arguments.  Then, the
	result of that invocation must be another procedure, which is
	also invoked with no arguments.  So, we have to define f as a
	procedure that returns a procedure:
		(define (f) (lambda () 'hello))	     ((f)) is  hello
		(define (f) (lambda () (+ 2 3)))     ((f)) is  5
	Or without the shorthand,
		(define f (lambda () (lambda () 'hello)))
		(define f (lambda () (lambda () (+ 2 3))))
	Alternatively, we can let the procedure f return some procedure
	we already know about, supposing that that procedure can be
	invoked with no arguments:
		(define (f) +)		             ((f)) is  0
		(define f (lambda () +))
	As a super tricky solution, for hotshots only, try this:
		(define (f) f)			     ((f)) is  #<procedure f>
						     (((f))) is.... ?

(((f)) 3)  Sheesh!  F has to be a function.  When we invoke it with no
	   arguments, we should get another function (let's call it G).
	   When we invoke G with no arguments, we get a third function
	   (call it H).  We have to be able to call H with the argument 3
	   and get some value.  We could spell this out as a sequence of
	   definitions like this:
		(define (h x) (* x x))
		(define (g) h)
		(define (f) g)			(((f)) 3) is  9
	   Alternatively, we can do this all in one:
		(define (f) (lambda () (lambda (x) (* x x))))
	   or without the abbreviation:
		(define f (lambda () (lambda () (lambda (x) (* x x)))))

By the way, you haven't yet learned the notation for functions that accept
any number of arguments, but if you did know it, you could use
		(define (f . args) f)
as the answer to *all* of these problems!


Problem 2:

This is a "do something to every word of a sentence" problem, like
PL-SENT or SQUARES, but with two extra arguments.  But it
also has a decision to make for each word (is this word equal to the
one we're replacing), like the filtering procedures EVENS, ENDS-E, etc.,
so it takes the form of a three-branch COND:

(define (substitute sent old new)
  (cond ((empty? sent) '())
	((equal? (first sent) old)
	 (se new (substitute (butfirst sent) old new)))
	(else (se (first sent) (substitute (butfirst sent) old new)))))


Problem 3:

Of course you could just try this on the computer, but you should understand
the results.

(t 1+) means that we should substitute the actual argument, which is the
function named 1+, for t's formal parameter, which is f, in t's body,
which is (lambda (x) (f (f (f x)))).  The result of the substitution is

		(lambda (x) (1+ (1+ (1+ x))))

Evaluating this produces a function that adds three to its argument, so
((t 1+) 0) is 3.

(t (t 1+)) means to substitute (t 1+) for f in t's body.  If we actually
substituted the lambda expression above for f three times, we'd get a
horrible mess:

		(lambda (x) ((lambda (x) (1+ (1+ (1+ x))))
			     ((lambda (x) (1+ (1+ (1+ x))))
			      ((lambda (x) (1+ (1+ (1+ x))))
			       0))))

but what's important is the function, not the expression that produced
the function, so we can just mentally give (t 1+) the name 3+ and then
the result we want is

		(lambda (x) (3+ (3+ (3+ x))))

and if we apply that function to 0 we'll get 9.

For the final piece of the problem, we have to begin by computing (t t), which
is what we get when we substitute t for f in t's body:

		(lambda (x) (t (t (t x))))

Don't be confused!  Even though this lambda expression has x as its formal
parameter, not f, the argument has to be a function, because we're going to
end up invoking t on that argument.  In other words, (t t) returns as its
value a function that takes a function as argument.

Now, ((t t) 1+) means to apply the function just above to the argument 1+
which, in turn, means to substitute 1+ for x in the body:

		(t (t (t 1+)))

Well, this isn't so hard; we've really already done it.  (t 1+) turned
out to be 3+, and (t (t 1+)) turned out to be 9+.  By the same reasoning,
this will turn out to be 27+ (that is, 9+ three times), so when we apply
this to 0 we get 27.

Problem 4:

This is actually the same as problem 2!  The function S is identical to
1+, so the answers have to be the same.  It's more work if you actually
substitute values into the body of s, but you can avoid all that if you
realize that these problems are identical in meaning.

Problem 5:

If (g) is a legal expression, then g takes ZERO arguments.
If ((g) 1) has the value 3, then (g) has a PROCEDURE as its value.
(If we'd asked for more than one word, you could say "a procedure
of one numeric argument that returns a number" or something.)


Problem 6:

(define (make-tester who)
  (lambda (x) (equal? x who)))



HOMEWORK:

Exercise 1.31(a):  

;; you only needed to hand in one version
;; but by now you're ready to understand both:

;; recursive version:

(define (product term a next b)
  (if (> a b)
      1       ;; Note multiplicative identity is 1 not 0
      (* (term a)
	 (product term (next a) next b))))

;; iterative version:

(define (product term a next b)
  (define (iter a result)
    (if (> a b)
	result
	(iter (next a) (* result (term a)))))
  (iter a 1))

;; factorial

(define (! n) (product (lambda (x) x) 1 1+ n))

;; pi
;; You have to run a few hundred terms to get a good approximation.
;; There are several possible ways to arrange the terms.  Here is one
;; way, in which the first term is 2/3, the second is 4/3, etc.

(define (pi terms) (* 4 (product
			 (lambda (x) (/ (* 2 (1+ (floor (/ x 2))))
					(1+ (* 2 (ceiling (/ x 2))))))
			 1 1+ terms)))

;; Here is another way, in which the first term is (2/3)*(4/3), the
;; second is (4/5)*(6/5), etc.  Notice that the value of a starts at
;; 3 and is increased by 2 for each new term.

(define (pi terms) (* 4 (product
			 (lambda (x) (/ (* (-1+ x) (1+ x))
					(* x x) ))
			 3
			 (lambda (x) (+ x 2))
			 terms )))

;; If you try to make it 2 * (4/3) * (4/3) * (6/5) * (6/5) * ... you'll
;; get the wrong answer, because you'll have one more number in the
;; numerator than in the denominator.



Exercise 1.32(a):  

;; you only needed to hand in one version

;; recursive form

(define (accumulate combiner null-value term a next b)
  (if (> a b)
      null-value
      (combiner (term a)
		(accumulate combiner null-value term (next a) next b))))

;; iterative form

(define (accumulate combiner null-value term a next b)
  (define (iter a result)
    (if (> a b)
	result
	(iter (next a) (combiner (term a) result))))
  (iter a null-value))

;; sum and product

(define (sum term a next b) (accumulate + 0 term a next b))

(define (product term a next b) (accumulate * 1 term a next b))



Exercise 1.33:  

;; The problem only requires one version but this too can be
;; recursive or iterative.  Recursive version:

(define (filtered-accumulate combiner null-value term a next b predicate)
  (cond ((> a b) null-value)
	((predicate a)
	 (combiner (term a)
		   (filtered-accumulate combiner
					null-value
					term
					(next a)
					next
					b
					predicate)))
	(else (filtered-accumulate combiner
				   null-value
				   term
				   (next a)
				   next
				   b
				   predicate))))

;; Iterative version:

(define (filtered-accumulate combiner null-value term a next b predicate)
  (define (iter a result)
    (cond ((> a b) result)
	  ((predicate a) (iter (next a) (combiner (term a) result)))
	  (else (iter (next a) result))))
  (iter a null-value))

;; (a) sum of squares of primes

(define (sum-sq-prime a b)
  (define (square x) (* x x))
  (filtered-accumulate + 0 square a 1+ b prime?))

;; (b) product of blah blah, using gcd from page 49

(define (prod-of-some-numbers n)
  (filtered-accumulate *
		       1
		       (lambda (x) x)
		       1
		       1+
		       n
		       (lambda (x) (= 1 (gcd x n)))))


Exercise 1.40:

(define (cubic a b c)
  (lambda (x) (+ (* x x x) (* a x x) (* b x) c)))


Exercise 1.41:

(define (double f)
  (lambda (x) (f (f x))))


Why does (((double (double double)) inc) 5) return 21 and not 13?
The crucial point is that DOUBLE is not associative.

> (((double (double double)) inc) 5)
21
> ((double (double (double inc))) 5)
13

DOUBLE turns a function into one that applies the function twice.
(DOUBLE DOUBLE) turns a function into one that applies the function
four times.
(DOUBLE (DOUBLE DOUBLE)) makes a function that applies (DOUBLE DOUBLE)
twice -- that is, make a function that applies the argument function
four times four times!



Exercise 1.43:  

(define (repeated f n)
  (lambda (x)
    (if (= n 0)
	x
	(f ((repeated f (- n 1)) x)))))

or

(define (repeated f n)
  (lambda (x)
    (if (= n 0)
	x
	((repeated f (- n 1)) (f x)))))

or

(define (repeated f n)
  (if (= n 0)
  (lambda (x) x)
  (lambda (x) (f ((repeated f (- n 1)) x)))))


We didn't assign 1.42, but if you followd the hint about it in 1.43,
you'd end up with this:

(define (repeated f n)
  (if (= n 0)
      (lambda (x) x)
      (compose f (repeated f (- n 1)))))


1.46

This problem is a little complicated in its details because there are so
many different procedures involved, with different domains and ranges.
But don't let that keep you from seeing the beauty of this extremely
general method!

(define (iterative-improve good-enough? improve)
  (define (iterate guess)
    (if (good-enough? guess)
	guess
	(iterate (improve guess))))
  iterate)

(define (sqrt x)  ;; compare to bottom of page 30 of SICP
  ((iterative-improve (lambda (guess) (< (abs (- (square guess) x)) 0.001))
		      (lambda (guess) (average guess (/ x guess))))
   1.0))

Some people were confused about sqrt because the original good-enough? takes
two arguments, and iterative-improve only allows for one.  But we are using
lexical scope so that the lambda expressions used as arguments to
iterative-improve have access to the starting value x.

(define (fixed-point f first-guess)  ;; compare to page 69
  ((iterative-improve (lambda (guess) (< (abs (- guess (f guess))) tolerance))
		      f)
   first-guess))

Here the structure is a little different from what's in the book, because
there is no variable NEXT to hold the next guess.  The solution above computes
(f guess) twice for each guess.  If you don't like that, you could use a more
complicated solution in which the argument to the good-enough procedure is a
sentence containing both old and new guesses:

(define (fixed-point f first-guess)
  ((iterative-improve (lambda (guesses)
			(< (abs (- (first guesses) (last guesses))) tolerance))
		      (lambda (guesses)
			(sentence (last guesses) (f (last guesses)))))
   (sentence first-guess (f first-guess))))

but I don't think the constant-factor efficiency improvement is worth the
added complexity of the code.


--------

2.  EVERY:

(define (every f sent)
  (if (empty? sent)
      '()
      (se (f (first sent))
	  (every f (butfirst sent)) )))


--------

Extra for experts:

This is a really hard problem!  But its solution comes up enough in the
literature that it has a name:  the Y combinator.  First here's the
combinator alone:

(lambda (f) (lambda (n) (f f n)))

And here's the factorial function using it:

(  (lambda (f) (lambda (n) (f f n)))
   (lambda (fun x)
     (if (= x 0)
	 1
	 (* x (fun fun (- x 1)))))  )

And now here's (fact 5):

(  (  (lambda (f) (lambda (n) (f f n)))
      (lambda (fun x)
	(if (= x 0)
	    1
	    (* x (fun fun (- x 1)))))  )
   5)

The trick is that instead of the factorial function taking a number as an
argument, it takes TWO arguments, a function (which will really be itself when
called) and a number.  The recursive call is done using the function provided
as argument.

The job of the Y combinator is to provide the function with itself as an
argument.

If that seems like a rabbit out of a hat, here's a longer explanation:

The problem we're trying to solve is that factorial wants to be able to call
itself recursively, and to do that it has to have a name for itself.  We have
two ways to give something a name, and one of them, DEFINE, is ruled out in
this problem.  That leaves procedure invocation, which associates formal
parameters (the names) with actual arguments (the values).  So we could
do this:

((lambda (f n) (if (= n 0) 1 (* n (f f (- n 1)))))
 (lambda (f n) (if (= n 0) 1 (* n (f f (- n 1)))))
 5)

to get the factorial of 5.  Ordinarily we think of factorial as a function
of one argument (N); here we've added a formal parameter F whose value is
another copy of the same function.  If you work through this expression,
you'll see that the first copy is called only for N=5; the other calls for
all smaller values of N use the second copy, because (unlike the first) it
is called with *itself* (the very same lambda-created procedure) as its
argument.

Now, it's a little ugly having to type the procedure twice.  Also, I sort of
half lied when I said there are only two ways to give something a name.
There's a kind of third way, LET, although that's really the same as creating
and calling a procedure; and LET is good at avoiding having to type something
twice.  So you might be tempted to say

(let ((fact (lambda (n) (if (= n 0) 1 (* n (fact (- n 1)))))))
  (fact 5))

But this doesn't work, because the name "fact" doesn't mean that lambda-
created procedure when the lambda expression is evaluated; that association
holds only in the body of the let.  If that isn't clear, we can expand it:

((lambda (fact) (FACT 5))
 (lambda (n) (if (= n 0) 1 (* n (fact (- n 1))))))

The capitalized FACT above is inside the lambda of which fact is the formal
parameter, so the (lambda (n) ...) procedure is substituted for it.  But the
name "fact" also appears on the second line of the expression, in the actual
argument expression, and *that* isn't inside the (lambda (fact) ...), so
there is no substitution; it will look for a global name fact.  Thus we have
to have F (in the original solution above) take *itself* as an argument, so
that the substitution happens in the right place.  We could do that with a
LET form equivalent to the original solution:

(let ((f (lambda (f n) (if (= n 0) 1 (* n (f f (- n 1)))))))
  (f f 5))

This one does work.  Notice that the body of the let, (f f 5), is almost
like the Y combinator's body, except that the latter generalizes to a
function of N instead of having 5 built in, like this LET expression:

(let ((f (lambda (f n) (if (= n 0) 1 (* n (f f (- n 1)))))))
  (lambda (n) (f f n)))

Now just rearrange this to eliminate the LET abbreviation:

((LAMBDA (F) (LAMBDA (N) (F F N)))
 (lambda (f n) (if (= n 0) 1 (* n (f f (- n 1))))))

This returns a function of N, the factorial function.  And the capitalized
part is the Y combinator.