-
Notifications
You must be signed in to change notification settings - Fork 50
/
sicp2.rkt
2011 lines (1626 loc) · 66 KB
/
sicp2.rkt
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
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;; -*- mode: racket; fill-column: 75; comment-column: 50; coding: utf-8; -*-
#lang racket
(require (lib "trace.ss"))
(define (inc a) (+ a 1))
(define (print-rat x)
(display (numer x))
(display '/)
(display (denom x))
(newline))
(define (numer x) (car x))
(define (denom x) (cdr x))
(define (add-rat x y)
(make-rat (+ (* (numer x) (denom y))
(* (numer y) (denom x)))
(* (denom x) (denom y))))
(define (sub-rat x y)
(make-rat (- (* (numer x) (denom y))
(* (numer y) (denom x)))
(* (denom x) (denom y))))
(define (mul-rat x y)
(make-rat (* (numer x) (numer y))
(* (denom x) (denom y))))
(define (div-rat x y)
(make-rat (* (numer x) (denom y))
(* (denom x) (numer y))))
(define (equal-rat? x y)
(= (* (numer x) (denom y))
(* (numer y) (denom x))))
(define (gcd a b)
(if (= b 0) a
(gcd b (remainder a b))))
#| Exercise: 2.1
Define a better version of `make-rat' that handles both positive and
negative arguments. `Make-rat' should normalize the sign so that if the
rational number is positive, both the numerator and denominator are
positive, and if the rational number is negative, only the numerator is
negative.
|#
(define (make-rat n d)
(let* [(g (abs (gcd n d)))
(nsign (xor (negative? d)
(negative? n)))
(num (/ (abs n) g))
(den (/ (abs d) g))]
(if nsign (cons (* -1 num) den)
(cons num den))))
#| Exercise: 2.2
Consider the problem of representing line segments in a plane. Each segment
is represented as a pair of points: a starting point and an ending point.
Define a constructor `make-segment' and selectors `start-segment' and
`end-segment' that define the representation of segments in terms of
points. Furthermore, a point can be represented as a pair of numbers: the x
coordinate and the y coordinate. Accordingly, specify a constructor
`make-point' and selectors `x-point' and `y-point' that define this
representation. Finally, using your selectors and constructors, define a
procedure `midpoint-segment' that takes a line segment as argument and
returns its midpoint (the point whose coordinates are the average of the
coordinates of the endpoints). To try your procedures, you'll need a way to
print points:
(define (print-point p)
(newline)
(display "(")
(display (x-point p))
(display ",")
(display (y-point p))
(display ")"))
|#
(struct coordinate (x y) #:transparent)
(struct segment (start end) #:transparent)
(define (midpoint segment)
(let [(mid-x (/ (+ (coordinate-x (segment-start segment))
(coordinate-x (segment-end segment))) 2))
(mid-y (/ (+ (coordinate-y (segment-start segment))
(coordinate-y (segment-end segment))) 2))]
(coordinate mid-x mid-y)))
;; alternative scheme
(define (make-point x y) `(,x . ,y))
(define (make-segment s e) `(,s . ,e))
(define (x-point p) (car p))
(define (y-point p) (cdr p))
(define (start-segment segment) (car segment))
(define (end-segment segment) (cdr segment))
(define (print-point p)
(display "(")
(display (x-point p))
(display ",")
(display (y-point p))
(display ")")
(newline))
(define (midpoint-s segment)
(make-segment
(/ (+ (x-point (start-segment segment))
(x-point (end-segment segment)))
2)
(/ (+ (y-point (start-segment segment))
(y-point (end-segment segment)))
2)))
#| Exercise 2.3
Implement a representation for rectangles in a plane. (Hint: You may want
to make use of *Note Exercise 2-2::.) In terms of your constructors and
selectors, create procedures that compute the perimeter and the area of a
given rectangle. Now implement a different representation for rectangles.
Can you design your system with suitable abstraction barriers, so that the
same perimeter and area procedures will work using either representation?
|#
(struct rectangle-s (height width)
#:guard (λ (height width type-name)
(if [and (segment? height) (segment? width)]
(values height width)
(error "not a valid rectangle"))))
(struct rectangle (a b)
#:guard (λ (a b type-name)
(if [and (coordinate? a) (coordinate? b)]
(values a b)
(error "not a valid rectangle"))))
(define (area rect)
(* (rect-height rect) (rect-width rect)))
(define (rect-height rect)
(abs (if (rectangle-s? rect)
(- (coordinate-y (segment-start (rectangle-s-height rect)))
(coordinate-y (segment-end (rectangle-s-height rect))))
(- (coordinate-y (rectangle-a rect))
(coordinate-y (rectangle-b rect))))))
(define (rect-width rect)
(abs (if (rectangle-s? rect)
(- (coordinate-x (segment-start (rectangle-s-width rect)))
(coordinate-x (segment-end (rectangle-s-width rect))))
(- (coordinate-x (rectangle-a rect))
(coordinate-x (rectangle-b rect))))))
#| Exercise: 2.4
Here is an alternative procedural representation
of pairs. For this representation, verify that `(car (cons x y))'
yields `x' for any objects `x' and `y'.
(define (cons x y)
(lambda (m) (m x y)))
(define (car z)
(z (lambda (p q) p)))
What is the corresponding definition of `cdr'? (Hint: To verify that this
works, make use of the substitution model of section *Note 1-1-5.)
|#
;;; whoa!!!
(define (recons x y)
(λ (m) (m x y)))
(define (recar z)
(z (λ (p q) p)))
(define (recdr z)
(z (λ (p q) q)))
#| Exercise: 2.5
Show that we can represent pairs of nonnegative integers using only numbers
and arithmetic operations if we represent the pair a and b as the integer
that is the product 2^a 3^b. Give the corresponding definitions of the
procedures `cons', `car', and `cdr'.
|#
(define lower-expt 2)
(define higher-expt 5)
(define (pack-pair a b)
(* (expt lower-expt a)
(expt higher-expt b)))
(define (unpack-base x base)
(if [= 0 (remainder x base)]
(+ 1 (unpack-base (/ x base) base))
0))
(define (unpack-pair d)
`(,(unpack-base d lower-expt)
,(unpack-base d higher-expt)))
#| Exercise: 2.6
In case representing pairs as procedures wasn't mind-boggling enough,
consider that, in a language that can manipulate procedures, we can get by
without numbers (at least insofar as nonnegative integers are concerned) by
implementing 0 and the operation of adding 1 as
(define zero (lambda (f) (lambda (x) x)))
|#
(define church-zero (λ (f) (λ (x) x)))
(define (church-add-1 n)
(λ (f) (λ (x) (f ((n f) x)))))
(define church-one
(λ (f)
(λ (x)
(f x))))
(define church-two
(λ (f)
(λ (x)
(f
(f x)))))
(define (church-addition m n)
(λ (f)
(λ (x)
((n f)
((m f)
x)))))
#| Exercise: 2.7
Alyssa's program is incomplete because she has not specified the
implementation of the interval abstraction. Here is a definition of the
interval constructor:
(define (make-interval a b) (cons a b))
Define selectors `upper-bound' and `lower-bound' to complete the
implementation.
|#
;; (module interval racket
;; (provide add-interval mul-interval div-interval)
;; (define (add-interval x y)
;; (make-interval (+ (lower-bound x) (lower-bound y))
;; (+ (upper-bound x) (upper-bound y))))
;; )
(define (make-interval a b) (cons a b))
(define (upper-bound interval) (cdr interval))
(define (lower-bound interval) (car interval))
#| Exercise: 2.8
Using reasoning analogous to Alyssa's, describe how the difference of two
intervals may be computed. Define a corresponding subtraction procedure,
called `sub-interval'.
|#
(define (sub-interval x y)
(let ((p1 (- (lower-bound x) (lower-bound y)))
(p2 (- (lower-bound y) (upper-bound x))))
(make-interval (min p1 p2)
(max p1 p2))))
#| Exercise: 2.9
The "width" of an interval is half of the difference between its upper and
lower bounds. The width is a measure of the uncertainty of the number
specified by the interval. For some arithmetic operations the width of the
result of combining two intervals is a function only of the widths of the
argument intervals, whereas for others the width of the combination is not
a function of the widths of the argument intervals. Show that the width of
the sum (or difference) of two intervals is a function only of the widths
of the intervals being added (or subtracted). Give examples to show that
this is not true for multiplication or division.
|#
#| Exercise: 2.10
Ben Bitdiddle, an expert systems programmer, looks over Alyssa's shoulder
and comments that it is not clear what it means to divide by an interval
that spans zero. Modify Alyssa's code to check for this condition and to
signal an error if it occurs.
|#
(define (div-interval x y)
(cond ((or (= 0 (upper-bound y)) (= 0 (lower-bound y)))
(error "attempted to divide by the zero"))
(else (mul-interval x
(make-interval (/ 1.0 (upper-bound y))
(/ 1.0 (lower-bound y)))))))
#| Exercise: 2.11
In passing, Ben also cryptically comments: "By testing the signs of the
endpoints of the intervals, it is possible to break `mul-interval' into
nine cases, only one of which requires more than two multiplications."
Rewrite this procedure using Ben's suggestion.
After debugging her program, Alyssa shows it to a potential user, who
complains that her program solves the wrong problem. He wants a program
that can deal with numbers represented as a center value and an additive
tolerance; for example, he wants to work with intervals such as 3.5 +/-
0.15 rather than [3.35, 3.65]. Alyssa returns to her desk and fixes this
problem by supplying an alternate constructor and alternate selectors:
(define (make-center-width c w) (make-interval (- c w) (+ c w)))
(define (center i) (/ (+ (lower-bound i) (upper-bound i)) 2))
(define (width i) (/ (- (upper-bound i) (lower-bound i)) 2))
Unfortunately, most of Alyssa's users are engineers. Real engineering
situations usually involve measurements with only a small uncertainty,
measured as the ratio of the width of the interval to the midpoint of the
interval. Engineers usually specify percentage tolerances on the parameters
of devices, as in the resistor specifications given earlier.
|#
(define (mul-interval x y)
(let ((p1 (* (lower-bound x) (lower-bound y)))
(p2 (* (lower-bound x) (upper-bound y)))
(p3 (* (upper-bound x) (lower-bound y)))
(p4 (* (upper-bound x) (upper-bound y))))
(make-interval (min p1 p2 p3 p4)
(max p1 p2 p3 p4))))
#| Exercise: 2.17
Define a procedure `last-pair' that returns the list that contains only the
last element of a given (nonempty) list:
(last-pair (list 23 72 149 34)) (34)
|#
(define (last-pair lst)
(let [(lastls (cdr lst))]
(if (null? lastls) (car lst)
(last-pair lastls))))
#| Exercise: 2.18
Define a procedure `reverse' that takes a list as argument and returns a
list of the same elements in reverse order:
(reverse (list 1 4 9 16 25)) (25 16 9 4 1)
|#
(define (reverse-l lst)
(if (null? lst) null
(append (reverse-l (cdr lst)) (list (car lst)))))
(define (reverse-ls xs [result null])
(cond [(null? xs) result]
[else (reverse-ls (cdr xs) (cons (car xs) result))]))
#| Exercise: 2.19
Consider the change-counting program of section *Note 1-2-2::. It would be
nice to be able to easily change the currency used by the program, so that
we could compute the number of ways to change a British pound, for example.
As the program is written, the knowledge of the currency is distributed
partly into the procedure `first-denomination' and partly into the
procedure `count-change' (which knows that there are five kinds of U.S.
coins). It would be nicer to be able to supply a list of coins to be used
for making change.
We want to rewrite the procedure `cc' so that its second argument is a list
of the values of the coins to use rather than an integer specifying which
coins to use. We could then have lists that defined each kind of currency:
(define us-coins (list 50 25 10 5 1))
(define uk-coins (list 100 50 20 10 5 2 1 0.5))
We could then call `cc' as follows:
(cc 100 us-coins) 292
To do this will require changing the program `cc' somewhat. It will still
have the same form, but it will access its second argument differently, as
follows:
(define (cc amount coin-values) (cond ((= amount 0) 1) ((or (< amount 0)
(no-more? coin-values)) 0) (else (+ (cc amount (except-first-denomination
coin-values)) (cc (- amount (first-denomination coin-values))
coin-values)))))
Define the procedures `first-denomination', `except-first-denomination',
and `no-more?' in terms of primitive operations on list structures. Does
the order of the list `coin-values' affect the answer produced by `cc'? Why
or why not?
|#
(define (valid-change n types)
(filter (lambda (x) (<= x n)) types))
(define (zv-count-change amt types)
(cond ((= amt 0) 1)
((or (< amt 0) (empty? (valid-change amt types))) 0)
(else (foldr (lambda (x res) (+ res (zv-count-change (- amt x))))
0
(valid-change amt types)))))
#| Exercise: 2.20
The procedures `+', `*', and `list' take arbitrary numbers of arguments.
One way to define such procedures is to use `define' with notation
"dotted-tail notation". In a procedure definition, a parameter list that
has a dot before the last parameter name indicates that, when the procedure
is called, the initial parameters (if any) will have as values the initial
arguments, as usual, but the final parameter's value will be a "list" of
any remaining arguments. For instance, given the definition
(define (f x y . z) <BODY>)
the procedure `f' can be called with two or more arguments. If we evaluate
(f 1 2 3 4 5 6)
then in the body of `f', `x' will be 1, `y' will be 2, and `z' will be the
list `(3 4 5 6)'. Given the definition
(define (g . w) <BODY>)
the procedure `g' can be called with zero or more arguments. If we evaluate
(g 1 2 3 4 5 6)
then in the body of `g', `w' will be the list `(1 2 3 4 5 6)'.(4)
Use this notation to write a procedure `same-parity' that takes one or more
integers and returns a list of all the arguments that have the same
even-odd parity as the first argument. For example,
(same-parity 1 2 3 4 5 6 7) (1 3 5 7)
(same-parity 2 3 4 5 6 7) (2 4 6)
|#
(define (same-parity elt . xs)
(define (test-parity n) (= (remainder elt 2) (remainder n 2)))
(filter test-parity xs))
#| Exercise: 2.21
The procedure `square-list' takes a list of numbers as argument and returns
a list of the squares of those numbers.
(square-list (list 1 2 3 4)) (1 4 9 16)
Here are two different definitions of `square-list'. Complete both of them
by filling in the missing expressions:
(define (square-list items) (if (null? items) nil (cons <??> <??>)))
(define (square-list items) (map <??> <??>))
|#
(define (square n) (* n n))
(define (square-list items)
(if (null? items) null
(cons (square (car items)) (square-list (cdr items)))))
(define (square-list-x items)
(map square items))
#| Exercise: 2.22
Louis Reasoner tries to rewrite the first `square-list' procedure of *Note
Exercise 2-21:: so that it evolves an iterative process:
(define (square-list items) (define (iter things answer) (if (null?
things) answer (iter (cdr things) (cons (square (car things)) answer))))
(iter items nil))
Unfortunately, defining `square-list' this way produces the answer list in
the reverse order of the one desired. Why?
Louis then tries to fix his bug by interchanging the arguments to `cons':
(define (square-list items) (define (iter things answer) (if (null? things)
answer (iter (cdr things) (cons answer (square (car things)))))) (iter
items nil))
This doesn't work either. Explain.
|#
;;; Louis Reasoner has mixed up the arguments `answer' and `(square (car things))'
;;; In his second attempt
;; correct version of iterative
;; (define (square-list-b things [answer null])
;; (if (null? things) answer
;; (square-list-b (cdr things)
;; (append answer (list (square (car things)))))))
#| Exercise: 2.23
The procedure `for-each' is similar to `map'. It takes as arguments a
procedure and a list of elements. However, rather than forming a list of
the results, `for-each' just applies the procedure to each of the elements
in turn, from left to right. The values returned by applying the procedure
to the elements are not used at all--`for-each' is used with procedures
that perform an action, such as printing. For example,
(for-each (lambda (x) (newline) (display x)) (list 57 321 88)) 57 321 88
The value returned by the call to `for-each' (not illustrated above) can be
something arbitrary, such as true. Give an implementation of `for-each'.
|#
(define (for-each-zv fn xs)
(if [empty? xs] null
(cons (fn (car xs))
(for-each-zv fn (cdr xs))))
#t)
;; not a exercize
(define (closest a b x)
(if (< (abs (- x (/ (numer a) (denom a))))
(abs (- x (/ (numer b) (denom b))))) a
b))
(define (find-closest-rational x limit)
(define (search-rationals n d top)
(cond [(> n limit) (search-rationals 0 (inc d) top)]
[(> d limit) top]
[else
(search-rationals (inc n)
d
(closest (make-rat n d) top x))]))
(search-rationals 1 1 (make-rat 1 1)))
(define (find-closest-rational-t x limit)
(define (search-rationals n d)
(if (or (> n limit) (> d limit)) (make-rat n d)
(closest (make-rat n d)
(closest
(search-rationals (inc n) d)
(search-rationals n (inc d))
x) x)))
(search-rationals 1 1))
(define (count-leaves x)
(cond ((null? x) 0)
((not (pair? x)) 1)
(else (+ (count-leaves (car x))
(count-leaves (cdr x))))))
#| Exercise: 2.24
Suppose we evaluate the expression `(list 1 (list 2 (list 3 4)))'. Give the
result printed by the interpreter, the corresponding box-and-pointer
structure, and the interpretation of this as a tree (as in *Note Figure
2-6::).
|#
#| Exercise: 2.25
Give combinations of `car's and `cdr's that will pick 7 from each of the
following lists:
(1 3 (5 7) 9)
((7))
(1 (2 (3 (4 (5 (6 7))))))
|#
(define (is-sevens)
[ printf "~a\n" (car (cdaddr '(1 3 (5 7) 9)))]
[ printf "~a\n" (caar '((7)))]
[ printf "~a\n" (cadadr (cadadr (cadadr '(1 (2 (3 (4 (5 (6 7)))))))))])
#| Exercise: 2.26
Suppose we define `x' and `y' to be two lists:
(define x (list 1 2 3))
(define y (list 4 5 6))
What result is printed by the interpreter in response to evaluating each of
the following expressions:
(append x y)
(cons x y)
(list x y)
|#
(define two-twentysix-x (list 1 2 3))
(define two-twentysix-y (list 4 5 6))
;;; (append two-twentysix-x two-twentysix-y) => '(1 2 3 4 5 6)
;;; (cons two-twentysix-x two-twentysix-y) => '((1 2 3) 4 5 6)
;;; (list two-twentysix-x two-twentysix-y) => '((1 2 3) (4 5 6))
#| Exercise: 2.27
Modify your `reverse' procedure of *Note Exercise 2-18:: to produce a
`deep-reverse' procedure that takes a list as argument and returns as its
value the list with its elements reversed and with all sublists
deep-reversed as well. For example,
(define x (list (list 1 2) (list 3 4)))
x ((1 2) (3 4))
(reverse x) ((3 4) (1 2))
(deep-reverse x) ((4 3) (2 1))
|#
(define (deep-reverse-l lst)
(cond [(null? lst) null]
[(list? lst) (append
(deep-reverse-l (rest lst))
(list (deep-reverse-l (first lst))))]
[else lst]))
#| Exercise: 2.28
Write a procedure `fringe' that takes as argument a tree (represented as a
list) and returns a list whose elements are all the leaves of the tree
arranged in left-to-right order. For example,
(define x (list (list 1 2) (list 3 4)))
(fringe x) (1 2 3 4)
(fringe (list x x)) (1 2 3 4 1 2 3 4)
|#
(define (fringe xs)
(cond [(null? xs) null]
[(list? xs) (append (fringe (first xs))
(fringe (rest xs)))]
[else (list xs)]))
#| Exercise: 2.29
A binary mobile consists of two branches, a left branch and a right branch.
Each branch is a rod of a certain length, from which hangs either a weight
or another binary mobile. We can represent a binary mobile using compound
data by constructing it from two branches (for example, using `list'):
(define (make-mobile left right) (list left right))
A branch is constructed from a `length' (which must be a number) together
with a `structure', which may be either a number (representing a simple
weight) or another mobile:
(define (make-branch length structure) (list length structure))
a. Write the corresponding selectors `left-branch' and `right-branch',
which return the branches of a mobile, and `branch-length' and
`branch-structure', which return the components of a branch.
b. Using your selectors, define a procedure `total-weight' that returns the
total weight of a mobile.
c. A mobile is said to be "balanced" if the torque applied by its top-left
branch is equal to that applied by its top-right branch (that is, if the
length of the left rod multiplied by the weight hanging from that rod is
equal to the corresponding product for the right side) and if each of the
submobiles hanging off its branches is balanced. Design a predicate that
tests whether a binary mobile is balanced.
d. Suppose we change the representation of mobiles so that the constructors
are
(define (make-mobile left right) (cons left right))
(define (make-branch length structure) (cons length structure))
How much do you need to change your programs to convert to the new
representation?
|#
;; Racket Style
(struct mobile (l r)
#:transparent)
(struct mbranch (len structure)
#:transparent)
(define (total-weight node)
(let [(mstruct (mbranch-structure node))]
(if (mobile? mstruct)
(+ (total-weight (mobile-l node))
(total-weight (mobile-r node)))
mstruct)))
(define (balanced-mobile? mbl)
(= (total-weight (mobile-l mbl))
(total-weight (mobile-r mbl))))
;;; Guile Style
(define (make-mobile left right) '(left right))
(define (make-branch len structure) '(len structure))
(define (sip-total-weight node)
(let [(mstruct (cadr node))]
(if (number? mstruct) mstruct
(+ (sip-total-weight (left-branch node))
(sip-total-weight (right-branch node))))))
(define (sip-balanced-mobile? mbl)
(= (total-weight (left-branch mbl))
(total-weight (right-branch mbl))))
#| Exercise: 2.30
Define a procedure `square-tree' analogous to the `square-list' procedure
of *Note Exercise 2-21::. That is, `square-list' should behave as follows:
(square-tree (list 1 (list 2 (list 3 4) 5) (list 6 7))) (1 (4 (9 16) 25)
(36 49))
Define `square-tree' both directly (i.e., without using any higher-order
procedures) and also by using `map' and recursion.
|#
(define (square-tree tree)
(map (λ (node)
(if (list? node) (square-tree node)
(* node node))) tree))
#| Exercise: 2.31
Abstract your answer to *Note Exercise 2-30:: to produce a procedure
`tree-map' with the property that `square-tree' could be defined as
(define (square-tree tree) (tree-map square tree))
We can represent a set as a list of distinct elements, and we can represent
the set of all subsets of the set as a list of lists. For example, if the
set is `(1 2 3)', then the set of all subsets is `(() (3) (2) (2 3) (1) (1
3) (1 2) (1 2 3))'. Complete the following definition of a procedure that
generates the set of subsets of a set and give a clear explanation of why
it works:
(define (subsets s) (if (null? s) (list nil) (let ((rest (subsets (cdr
s)))) (append rest (map <??> rest)))))
|#
(define (tree-map fn tree)
(map (λ (node)
(if (list? node) (tree-map fn node)
(fn node))) tree))
#| Exercise: 2.32
|#
(define (subsets s)
(if (null? s) (list null)
(let [(restl (subsets (cdr s)))]
(append restl (map (λ (x) (cons (car s) x)) restl)))))
;; -- UTILITIES -------------------------------------
(define (filter predicate sequence)
(cond ((null? sequence) null)
((predicate (car sequence))
(cons (car sequence)
(filter predicate (cdr sequence))))
(else (filter predicate (cdr sequence)))))
(define (accumulate op initial sequence)
(if (null? sequence)
initial
(op (car sequence)
(accumulate op initial (cdr sequence)))))
(define (flatmap proc seq)
(accumulate append null (map proc seq)))
(define (permutations s)
(if (null? s) ; empty set?
(list null) ; sequence containing empty set
(flatmap (lambda (x)
(map (lambda (p) (cons x p))
(permutations (remove x s))))
s)))
;; --------------------------------------------------
#| Exercise: 2.33
|#
(define (map-z p sequence)
(accumulate (λ (x y) (cons (p x) y)) null sequence))
(define (append-z seq1 seq2)
(accumulate cons seq2 seq1))
(define (length-z sequence)
(accumulate (λ (x y) (+ y 1)) 0 sequence))
#| Exercise: 2.34
Evaluating a polynomial in x at a given value of x can be formulated as an
accumulation. We evaluate the polynomial
a_n r^n | a_(n-1) r^(n-1) + ... + a_1 r + a_0
using a well-known algorithm called "Horner's rule", which structures the
computation as
(... (a_n r + a_(n-1)) r + ... + a_1) r + a_0
In other words, we start with a_n, multiply by x, add a_(n-1), multiply by
x, and so on, until we reach a_0.(3)
Fill in the following template to produce a procedure that evaluates a
polynomial using Horner's rule. Assume that the coefficients of the
polynomial are arranged in a sequence, from a_0 through a_n.
(define (horner-eval x coefficient-sequence) (accumulate (lambda
(this-coeff higher-terms) <??>) 0 coefficient-sequence))
For example, to compute 1 + 3x + 5x^3 + x^(5) at x = 2 you would evaluate
(horner-eval 2 (list 1 3 0 5 0 1))
|#
(define (horner-eval x coefficient-sequence)
(accumulate (λ (this-coeff higher-terms)
(+ this-coeff (* x higher-terms)))
0
coefficient-sequence))
#| Exercise: 2.35
Redefine `count-leaves' from section *Note 2-2-2:: as an accumulation:
(define (count-leaves t) (accumulate <??> <??> (map <??> <??>)))
|#
(define (count-leaves-z t)
(accumulate + 0 (map count-leaves t)))
#| Exercise: 2.36
The procedure `accumulate-n' is similar to `accumulate' except that it
takes as its third argument a sequence of sequences, which are all assumed
to have the same number of elements. It applies the designated accumulation
procedure to combine all the first elements of the sequences, all the
second elements of the sequences, and so on, and returns a sequence of the
results. For instance, if `s' is a sequence containing four sequences, `((1
2 3) (4 5 6) (7 8 9) (10 11 12)),' then the value of `(accumulate-n + 0 s)'
should be the sequence `(22 26 30)'. Fill in the missing expressions in the
following definition of `accumulate-n':
(define (accumulate-n op init seqs) (if (null? (car seqs)) nil (cons
(accumulate op init <??>) (accumulate-n op init <??>))))
|#
(define (accumulate-n op ini seqs)
(if (null? (car seqs))
null
(cons (accumulate op ini (map (lambda (x) (car x)) seqs))
(accumulate-n op ini (map (lambda (x) (cdr x)) seqs)))))
#| Exercise: 2.37
Exercise 2.37 .............
Suppose we represent vectors v = (v_i) as sequences of numbers, and
matrices m = (m_(ij)) as sequences of vectors (the rows of the matrix). For
example, the matrix
+- -+ | 1 2 3 4 | | 4 5 6 6 | | 6 7 8 9 | +- -+
is represented as the sequence `((1 2 3 4) (4 5 6 6) (6 7 8 9))'. With this
representation, we can use sequence operations to concisely express the
basic matrix and vector operations. These operations (which are described
in any book on matrix algebra) are the following:
__ (dot-product v w) returns the sum >_i v_i w_i
(matrix-*-vector m v) returns the vector t, __ where t_i = >_j m_(ij) v_j
(matrix-*-matrix m n) returns the matrix p, __ where p_(ij) = >_k m_(ik)
n_(kj)
(transpose m) returns the matrix n, where n_(ij) = m_(ji)
We can define the dot product as(4)
(define (dot-product v w) (accumulate + 0 (map * v w)))
Fill in the missing expressions in the following procedures for computing
the other matrix operations. (The procedure `accumulate-n' is defined in
*Note Exercise 2-36::.)
(define (matrix-*-vector m v) (map <??> m))
(define (transpose mat) (accumulate-n <??> <??> mat))
(define (matrix-*-matrix m n) (let ((cols (transpose n))) (map <??> m)))
|#
(define zv-matrix '((1 2 3 4) (4 5 6 6) (6 7 8 9)))
(define zv-square '((1 2 3) (4 5 6) (6 7 8)))
(define (dot-product v w)
(accumulate + 0 (map * v w)))
(define (matrix-*-vector m v)
(map (lambda (row) (dot-product row v)) m))
(define (transpose mat)
(accumulate-n cons '() mat))
(define (matrix-*-matrix m n)
(let [(elems (transpose n))]
(map (λ (row) (matrix-*-vector elems row)) m)))
#| Exercise: 2.38
The `accumulate' procedure is also known as `fold-right', because it
combines the first element of the sequence with the result of combining all
the elements to the right. There is also a `fold-left', which is similar to
`fold-right', except that it combines elements working in the opposite
direction:
(define (fold-left op initial sequence) (define (iter result rest) (if
(null? rest) result (iter (op result (car rest)) (cdr rest)))) (iter
initial sequence))
What are the values of
(fold-right / 1 (list 1 2 3))
(fold-left / 1 (list 1 2 3))
(fold-right list nil (list 1 2 3))
(fold-left list nil (list 1 2 3))
Give a property that `op' should satisfy to guarantee that `fold-right' and
`fold-left' will produce the same values for any sequence.
|#
;;;; skipped
#| Exercise: 2.39
Complete the following definitions of `reverse' (*Note Exercise 2-18::) in
terms of `fold-right' and `fold-left' from *Note Exercise 2-38:::
(define (reverse sequence) (fold-right (lambda (x y) <??>) nil sequence))
(define (reverse sequence) (fold-left (lambda (x y) <??>) nil sequence))
|#
(define (reverse-fr sequence)
(foldr (lambda (x y) (append y `(,x))) null sequence))
(define (reverse-fl sequence)
(foldl (lambda (x y) (cons x y)) null sequence))
#| Exercise: 2.40
Define a procedure `unique-pairs' that, given an integer n, generates the
sequence of pairs (i,j) with 1 <= j< i <= n. Use `unique-pairs' to simplify
the definition of `prime-sum-pairs' given above.
|#
(define (unique-pairs n)
(flatmap (λ (i)
(map (λ (j) (list i j))
(range i n)))
(range 1 n)))
(define (prime? n)
(empty?
(filter (lambda (p) (= n (* (car p) (cadr p))))
(unique-pairs n))))
(define (prime-sum? pair) (prime? (+ (car pair) (cadr pair))))
(define (make-pair-sum pair)
(list (car pair) (cadr pair) (+ (car pair) (cadr pair))))
(define (prime-sum-pairs n)
(map make-pair-sum (filter prime-sum? (unique-pairs n))))
#| Exercise: 2.41
Write a procedure to find all ordered triples of distinct positive integers
i, j, and k less than or equal to a given integer n that sum to a given
integer s.
|#
(define (triplets-summing-to s n)
(define (unique-triplets n)
(flatmap (λ (i)
(flatmap (λ (j)
(map (λ (k)
(list i j k))
(range j n)))
(range i n)))
(range 0 n)))
(filter (λ (t) (= s (foldr + 0 t)))
(unique-triplets n)))
#| Exercise: 2.42
The "eight-queens puzzle" asks how to place eight queens on a chessboard so
that no queen is in check from any other (i.e., no two queens are in the
same row, column, or diagonal). One possible solution is shown in *Note
Figure 2-8. One way to solve the puzzle is to work across the board,
placing a queen in each column. Once we have placed k - 1 queens, we must
place the kth queen in a position where it does not check any of the queens
already on the board. We can formulate this approach recursively: Assume
that we have already generated the sequence of all possible ways to place k
- 1 queens in the first k - 1 columns of the board. For each of these ways,
generate an extended set of positions by placing a queen in each row of the
kth column. Now filter these, keeping only the positions for which the
queen in the kth column is safe with respect to the other queens. This
produces the sequence of all ways to place k queens in the first k columns.
By continuing this process, we will produce not only one solution, but all
solutions to the puzzle.
We implement this solution as a procedure `queens', which returns a
sequence of all solutions to the problem of placing n queens on an n*n
chessboard. `Queens' has an internal procedure `queen-cols' that returns
the sequence of all ways to place queens in the first k columns of the
board.
(define (queens board-size) (define (queen-cols k) (if (= k 0) (list
empty-board) (filter (lambda (positions) (safe? k positions)) (flatmap
(lambda (rest-of-queens) (map (lambda (new-row) (adjoin-position new-row k
rest-of-queens)) (enumerate-interval 1 board-size))) (queen-cols (- k
1)))))) (queen-cols board-size))
In this procedure `rest-of-queens' is a way to place k - 1 queens in the
first k - 1 columns, and `new-row' is a proposed row in which to place the
queen for the kth column. Complete the program by implementing the
representation for sets of board positions, including the procedure
`adjoin-position', which adjoins a new row-column position to a set of
positions, and `empty-board', which represents an empty set of positions.
You must also write the procedure `safe?', which determines for a set of
positions, whether the queen in the kth column is safe with respect to the