计算机程序的构造和解释(SICP) 第3章 习题解析 Part4
这次回顾第三章第四部分习题。
学习资料:
https://github.com/DeathKing/Learning-SICP
https://mitpress.mit.edu/sites/default/files/sicp/index.html
https://www.bilibili.com/video/BV1Xx41117tr?from=search&seid=14983483066585274454
参考资料:
https://sicp.readthedocs.io/en/latest
http://community.schemewiki.org/?SICP-Solutions
http://community.schemewiki.org/?sicp
3.33(p205)
(define (averager a b c)
(let ((d (make-connector))
(e (make-connector)))
(adder a b d)
(constant 2 e)
(multiplier d e c)))
3.34(p205)
参考资料:
https://sicp.readthedocs.io/en/latest/chp3/34.html
代码
(load "constraints.scm")
(define (squarer a b)
(multiplier a a b))
(define a (make-connector))
(define b (make-connector))
(squarer a b)
(probe "a" a)
(probe "b" b)
(set-value! a 1 'user)
(newline)
(forget-value! a 'user)
(newline)
(set-value! b 2 'user)
(newline)
(exit)
实验结果:
Probe: a = 1
Probe: b = 1
Probe: a = ?
Probe: b = ?
Probe: b = 2
没有给$a$赋值的原因是,process-new-value无法给$m_1,m_2$都没值的情形赋值(注意到对于平方的情形,这种情况是可以赋值的):
(define (multiplier m1 m2 product)
(define (process-new-value)
(cond ((or (and (has-value? m1) (= (get-value m1) 0))
(and (has-value? m2) (= (get-value m2) 0)))
(set-value! product 0 me))
((and (has-value? m1) (has-value? m2))
(set-value! product
(* (get-value m1) (get-value m2))
me))
((and (has-value? product) (has-value? m1))
(set-value! m2
(/ (get-value product) (get-value m1))
me))
((and (has-value? product) (has-value? m2))
(set-value! m1
(/ (get-value product) (get-value m2))
me))))
(define (process-forget-value)
(forget-value! product me)
(forget-value! m1 me)
(forget-value! m2 me)
(process-new-value))
(define (me request)
(cond ((eq? request 'I-have-a-value)
(process-new-value))
((eq? request 'I-lost-my-value)
(process-forget-value))
(else
(error "Unknown request -- MULTIPLIER" request))))
(connect m1 me)
(connect m2 me)
(connect product me)
me)
3.35(p205)
参考资料:
https://sicp.readthedocs.io/en/latest/chp3/35.html
对之前的问题进行修改:
(load "constraints.scm")
(define (square a)
(* a a))
(define (squarer a b)
(define (process-new-value)
(cond ((and (has-value? b) (< (get-value b) 0))
(error "square less than 0 -- SQUARER" (get-value b)))
((and (has-value? b) (>= (get-value b) 0))
(set-value! a (sqrt (get-value b)) me))
((has-value? a)
(set-value! b (square (get-value a)) me))))
(define (process-forget-value)
(forget-value! a me)
(forget-value! b me)
(process-new-value))
(define (me request)
(cond ((eq? request 'I-have-a-value)
(process-new-value))
((eq? request 'I-lost-my-value)
(process-forget-value))
(else
(error "Unknown request -- SQUARER" request))))
(connect a me)
(connect b me)
me)
(define a (make-connector))
(define b (make-connector))
(squarer a b)
(probe "a" a)
(newline)
(probe "b" b)
(newline)
(set-value! a 1 'user)
(newline)
(forget-value! a 'user)
(newline)
(set-value! b 2 'user)
(exit)
结果如下:
Probe: a = 1
Probe: b = 1
Probe: a = ?
Probe: b = ?
Probe: b = 2
Probe: a = 1.4142135623730951
3.36(p205)
参考资料:
https://sicp.readthedocs.io/en/latest/chp3/36.html
3.37(p205)
(load "constraints.scm")
(define (c+ x y)
(let ((z (make-connector)))
(adder x y z)
z))
(define (c* x y)
(let ((z (make-connector)))
(multiplier x y z)
z))
(define (c/ x y)
(let ((z (make-connector)))
(multiplier y z x)
z))
(define (cv v)
(let ((z (make-connector)))
(constant v z)
z))
; test
(define (celsius-fahrenheit-converter x)
(c+ (c* (c/ (cv 9) (cv 5))
x)
(cv 32)))
(define C (make-connector))
(define F (celsius-fahrenheit-converter C))
(set-value! C 5 'user)
(newline)
(probe "C" C)
(probe "F" F)
(newline)
(exit)
结果如下:
Probe: C = 5
Probe: F = 41
3.38(p210)
(a)
三种操作一共有六种排列,因此可能的结果如下:
100 -> 110 -> 90 -> 45
100 -> 80 -> 90 -> 45
100 -> 110 -> 55 -> 35
100 -> 80 -> 40 -> 50
100 -> 50 -> 60 -> 40
100 -> 50 -> 30 -> 40
(b)
此时区别在于会出现并发的情况:
两个操作并发:
100 -> 110 -> 90 / 55
100 -> 80 -> 90 / 40
100 -> 50 -> 60 / 30
三个操作并发:
100 -> 110 / 80 /50
3.39(p212)
参考资料:
https://sicp.readthedocs.io/en/latest/chp3/39.html
下面两种计算过程是显然的:
10 -> (set! x ((s (lambda () (* x x))))) = 100 -> (set! x (+ x 1)) = 101
10 -> (set! x (+ x 1)) = 11 -> (set! x ((s (lambda () (* x x))))) = 121
最后一种计算过程为:
10 -> (set! x ((s (lambda () (* x x))))) = 100
-> (set! x (+ x 1)) = 11
这种情形对应并发运行,所以最终的结果为$11$或$100$。
3.40(p212)
全部情形:
10 -> (set! x (* x x)) = 100 -> (set! x (* x x x)) = 1000000
10 -> (set! x (* x x x)) = 1000 -> (set! x (* x x)) = 1000000
10 -> (set! x (* x x)) = 100
-> (set! x (* x x x)) = 1000
10 -> (set! x (* 10 100 100)) = 100000
-> (set! x (* 10 10 100)) = 10000
10 -> (set! x (* 10 1000)) = 10000
如果改成串行化,那么结果可能为:
10 -> (set! x (* x x)) = 100 -> (set! x (* x x x)) = 1000000
10 -> (set! x (* x x x)) = 1000 -> (set! x (* x x)) = 1000000
3.41(p213)
参考资料:
http://community.schemewiki.org/?sicp-ex-3.41
不正确,因为balance操作不改变balance的值,所以不需要加入串行组。
3.42(p213)
参考资料:
https://sicp.readthedocs.io/en/latest/chp3/42.html
http://community.schemewiki.org/?sicp-ex-3.42
这样修改的疑问是,如果调用如下语句会发生什么
(parallel-execute
((account 'deposit) 10)
((account 'deposit) 10)
)
即,两个protected-withdraw能否会并发运行,个人倾向于两者不能并发运行,因此这样修改是有问题的。