Chinaunix

标题: SICP 习题参考答案 3.3 [打印本页]

作者: win_hate    时间: 2008-12-13 10:18
标题: SICP 习题参考答案 3.3
此帖为 SICP 章节 3.3 的参考答案。为保持可读性,请勿直接在本帖讨论。讨论请另开新帖。



作者: win_hate    时间: 2008-12-13 10:20
标题: Exercise 3.12.
Exercise 3.12.  The following procedure for appending lists was introduced in section 2.2.1:

(define (append x y)
  (if (null? x)
      y
      (cons (car x) (append (cdr x) y))))

Append forms a new list by successively consing the elements of x onto y. The procedure append! is similar to append, but it is a mutator rather than a constructor. It appends the lists by splicing them together, modifying the final pair of x so that its cdr is now y. (It is an error to call append! with an empty x.)

(define (append! x y)
  (set-cdr! (last-pair x) y)
  x)

Here last-pair is a procedure that returns the last pair in its argument:

(define (last-pair x)
  (if (null? (cdr x))
      x
      (last-pair (cdr x))))

Consider the interaction

(define x (list 'a 'b))
(define y (list 'c 'd))
(define z (append x y))
z
(a b c d)
(cdr x)
<response>
(define w (append! x y))
w
(a b c d)
(cdr x)
<response>

What are the missing <response>s? Draw box-and-pointer diagrams to explain your answer.
作者: win_hate    时间: 2008-12-13 10:31
标题: Exercise 3.12. 答案

作者: win_hate    时间: 2008-12-13 10:34
标题: Exercise 3.13.
Exercise 3.13.  Consider the following make-cycle procedure, which uses the last-pair procedure defined in exercise 3.12:

(define (make-cycle x)
  (set-cdr! (last-pair x) x)
  x)

Draw a box-and-pointer diagram that shows the structure z created by

(define z (make-cycle (list 'a 'b 'c)))

What happens if we try to compute (last-pair z)?
作者: win_hate    时间: 2008-12-13 10:37
标题: Exercise 3.13. 答案

作者: win_hate    时间: 2008-12-13 10:38
标题: Exercise 3.14.
Exercise 3.14.  The following procedure is quite useful, although obscure:

(define (mystery x)
  (define (loop x y)
    (if (null? x)
        y
        (let ((temp (cdr x)))
          (set-cdr! x y)
          (loop temp x))))
  (loop x '()))

Loop uses the ``temporary'' variable temp to hold the old value of the cdr of x, since the set-cdr! on the next line destroys the cdr. Explain what mystery does in general. Suppose v is defined by (define v (list 'a 'b 'c 'd)). Draw the box-and-pointer diagram that represents the list to which v is bound. Suppose that we now evaluate (define w (mystery v)). Draw box-and-pointer diagrams that show the structures v and w after evaluating this expression. What would be printed as the values of v and w ?
作者: win_hate    时间: 2008-12-13 10:54
标题: Exercise 3.14. 答案

作者: win_hate    时间: 2008-12-14 11:36
标题: Exercise 3.15.
Exercise 3.15.  Draw box-and-pointer diagrams to explain the effect of set-to-wow! on the structures z1 and z2 above.
作者: win_hate    时间: 2008-12-14 11:36
标题: Exercise 3.15. 答案
<wait: pic>
作者: win_hate    时间: 2008-12-14 11:37
标题: Exercise 3.16.
Exercise 3.16.  Ben Bitdiddle decides to write a procedure to count the number of pairs in any list structure. ``It's easy,'' he reasons. ``The number of pairs in any structure is the number in the car plus the number in the cdr plus one more to count the current pair.'' So Ben writes the following procedure:

(define (count-pairs x)
  (if (not (pair? x))
      0
      (+ (count-pairs (car x))
         (count-pairs (cdr x))
         1)))

Show that this procedure is not correct. In particular, draw box-and-pointer diagrams representing list structures made up of exactly three pairs for which Ben's procedure would return 3; return 4; return 7; never return at all.
作者: win_hate    时间: 2008-12-14 11:40
标题: Exercise 3.16. 答案
造成计数混乱的原因是数据中有共享部分,这些共享部分被重复计算。


  1. (define s3 (cons (cons 1 2) (cons 3 4)))

  2. (define a (cons 2 3))
  3. (define b (cons a a))
  4. (define s4 (cons 1 b))
  5. (define s7 (cons b b))
复制代码


测试


  1. > (load "3.16.scm")

  2. > (count-pairs s3)
  3. 3

  4. > (count-pairs s4)
  5. 4

  6. > (count-pairs s7)
  7. 7
复制代码

作者: win_hate    时间: 2008-12-14 12:20
标题: Exercise 3.17.
Exercise 3.17.  Devise a correct version of the count-pairs procedure of exercise 3.16 that returns the number of distinct pairs in any structure. (Hint: Traverse the structure, maintaining an auxiliary data structure that is used to keep track of which pairs have already been counted.)
作者: win_hate    时间: 2008-12-14 12:33
标题: Exercise 3.17. 答案
最直接的想法是记住已经访问过的部分。可以把已经访问过的节点(地址)存放在一个 hash 表内。


  1. (define (count-pairs x)
  2.   (let ((table (make-hash-table)))
  3.     (define (f x)
  4.       (cond ((not (pair? x)) 0)
  5.             ((hashq-ref table x) 0)
  6.             (else  (hashq-set! table x #t)
  7.                    (+ (f (car x)) (f (cdr x)) 1))))
  8.     (f x)))
复制代码


这里的 make-hash-table 是 guile 里提供的. 哈希表不是 scheme 的标准组成部分,所以不同的 shceme 中的 hash 接口并不一致。

测试


  1. > (load "3.16.scm")
  2. > (load "3.17.scm")

  3. > (count-pairs s3)
  4. 3

  5. > (count-pairs s4)
  6. 3

  7. > (count-pairs s7)
  8. 3
复制代码


如果用 3.13 中的 make-cycle 生成一个循环列表,则 3.16 中的 count-pairs 不会返回,而这里的 count-pairs 能返回。


  1. > (define x (make-cycle (list 1 2 3 4 5 6 7)))

  2. > (count-pairs x)
  3. 7

  4. > (define y (make-cycle (list 1 1 1 1 1 1 1)))

  5. > (count-pairs x)
  6. 7
复制代码

[ 本帖最后由 win_hate 于 2008-12-14 12:42 编辑 ]
作者: win_hate    时间: 2008-12-14 12:43
标题: Exercise 3.18.
Exercise 3.18.  Write a procedure that examines a list and determines whether it contains a cycle, that is, whether a program that tried to find the end of the list by taking successive cdrs would go into an infinite loop. Exercise 3.13 constructed such lists.
作者: win_hate    时间: 2008-12-14 12:44
标题: Exercise 3.18. 答案
跟上一题类似的思路


  1. (define (check-cycle xs)
  2.   (let ((table (make-hash-table)))
  3.     (define (f ys)
  4.       (cond ((null? ys) '#f)
  5.             ((hashq-ref table ys) '#t)
  6.             (else (hashq-set! table ys #t)
  7.                   (f (cdr ys)))))
  8.     (f xs)))
复制代码


测试


  1. > (load "3.13.scm")
  2. > (load "3.18.scm")

  3. > (define x (make-cycle (list 1 2 3 4 5 6 7)))
  4. > (check-cycle x)
  5. #t

  6. > (define y (make-cycle (list 1 1 1 1 1 1 1)))
  7. > (check-cycle y)
  8. #t

  9. > (define z (list 1 2 3 4 5 6 7))
  10. > (check-cycle z)
  11. #f
复制代码

[ 本帖最后由 win_hate 于 2008-12-14 12:46 编辑 ]
作者: win_hate    时间: 2008-12-14 12:49
标题: Exercise 3.19.
Exercise 3.19.  Redo exercise 3.18 using an algorithm that takes only a constant amount of space. (This requires a very clever idea.)
作者: win_hate    时间: 2008-12-14 13:18
标题: Exercise 3.19. 答案

  1. (define (check-cycle xs)
  2.   (define (f s1 s2)
  3.     (cond ((null? s2) #f)
  4.           ((eq? s1 s2) #t)
  5.           ((null? (cdr s2)) #f)
  6.           (else (f (cdr s1) (cddr s2)))))
  7.            
  8.   (cond ((null? xs) #f)
  9.         ((null? (cdr xs)) #f)
  10.         (else (f xs (cddr xs)))))
复制代码

解释:


关于这个题目,在 CU 上有更深入一些的讨论,请参考


[ 本帖最后由 win_hate 于 2008-12-14 13:30 编辑 ]
作者: win_hate    时间: 2008-12-19 20:31
标题: Exercise 3.20.
Exercise 3.20.  Draw environment diagrams to illustrate the evaluation of the sequence of expressions

(define x (cons 1 2))
(define z (cons x x))
(set-car! (cdr z) 17)
(car x)
17

using the procedural implementation of pairs given above. (Compare exercise 3.11.)
作者: win_hate    时间: 2008-12-19 20:32
标题: Exercise 3.20. 答案
<wait for pic>
作者: win_hate    时间: 2008-12-19 20:33
标题: Exercise 3.21.
Exercise 3.21.  Ben Bitdiddle decides to test the queue implementation described above. He types in the procedures to the Lisp interpreter and proceeds to try them out:

(define q1 (make-queue))
(insert-queue! q1 'a)
((a) a)
(insert-queue! q1 'b)
((a b) b)
(delete-queue! q1)
((b) b)
(delete-queue! q1)
(() b)

``It's all wrong!'' he complains. ``The interpreter's response shows that the last item is inserted into the queue twice. And when I delete both items, the second b is still there, so the queue isn't empty, even though it's supposed to be.'' Eva Lu Ator suggests that Ben has misunderstood what is happening. ``It's not that the items are going into the queue twice,'' she explains. ``It's just that the standard Lisp printer doesn't know how to make sense of the queue representation. If you want to see the queue printed correctly, you'll have to define your own print procedure for queues.'' Explain what Eva Lu is talking about. In particular, show why Ben's examples produce the printed results that they do. Define a procedure print-queue that takes a queue as input and prints the sequence of items in the queue.
作者: win_hate    时间: 2008-12-19 20:39
标题: Exercise 3.21. 答案
由于 queue 结构在最顶端是两个指针,即一个 cons。第一个指针指向队列头,所以打印出整个队列,第二个指针指向队列尾,所以只打印出队列的最后一项。


  1. (define (print-queue queue)
  2.   (display (front-ptr queue))
  3.   (newline))
复制代码


演示


  1. > (define Q (make-queue))

  2. > (insert-queue! Q 'a)
  3. ((a) a)

  4. > (insert-queue! Q 'b)
  5. ((a b) b)

  6. > (insert-queue! Q 'c)
  7. ((a b c) c)

  8. > (print-queue Q)
  9. (a b c)

  10. > (delete-queue! Q)
  11. ((b c) c)

  12. > (print-queue Q)
  13. (b c)
复制代码

作者: win_hate    时间: 2008-12-19 20:40
标题: Exercise 3.22.
Exercise 3.22.  Instead of representing a queue as a pair of pointers, we can build a queue as a procedure with local state. The local state will consist of pointers to the beginning and the end of an ordinary list. Thus, the make-queue procedure will have the form

(define (make-queue)
  (let ((front-ptr ...)
        (rear-ptr ...))
    <definitions of internal procedures>
    (define (dispatch m) ...)
    dispatch))

Complete the definition of make-queue and provide implementations of the queue operations using this representation.
作者: win_hate    时间: 2008-12-19 20:48
标题: Exercise 3.22. 答案

  1. (define (make-queue)
  2.   (let ((front-ptr '())
  3.         (rear-ptr '()))

  4.     (define (empty-queue?)
  5.       (eq? front-ptr '()))
  6.    
  7.     (define (insert-queue item)
  8.       (cond ((empty-queue?)
  9.              (set! front-ptr item)
  10.              (set! rear-ptr item))
  11.             (else
  12.              (set-cdr! rear-ptr item)
  13.              (set! rear-ptr item))))

  14.     (define (delete-queue)
  15.       (cond ((empty-queue?)
  16.              (error "DELETE! called with an empty queue"))
  17.             (else
  18.              (set! front-ptr (cdr front-ptr)))))
  19.    
  20.     (define (front-queue)
  21.       (if (empty-queue?)
  22.           (error "FRONT called with an empty queue")
  23.           (car front-ptr)))

  24.     (define (dispatch m . z)
  25.       (cond ((eq? m 'front-ptr) front-ptr)
  26.             ((eq? m 'rear-ptr) rear-ptr)
  27.             ((eq? m 'empty?) (empty-queue?))
  28.             ((eq? m 'front) (front-queue))
  29.             ((eq? m 'insert) (insert-queue z))
  30.             ((eq? m 'delete) (delete-queue))
  31.             (else (error "Unknown command" m))))
  32.       
  33.   dispatch))
复制代码


演示


  1. > (define Q (make-queue))

  2. > (Q 'empty?)
  3. #t

  4. > (Q 'print)
  5. ()

  6. > (Q 'insert 'a)

  7. > (Q 'empty?)
  8. #f

  9. > (Q 'insert 'b)
  10. > (Q 'insert 'c)

  11. > (Q 'front)
  12. a

  13. > (Q 'print)
  14. (a b c)

  15. > (Q 'delete)

  16. > (Q 'print)
  17. (b c)
复制代码

作者: win_hate    时间: 2008-12-20 11:34
标题: Exercise 3.23.
Exercise 3.23.  A deque (``double-ended queue'') is a sequence in which items can be inserted and deleted at either the front or the rear. Operations on deques are the constructor make-deque, the predicate empty-deque?, selectors front-deque and rear-deque, and mutators front-insert-deque!, rear-insert-deque!, front-delete-deque!, and rear-delete-deque!. Show how to represent deques using pairs, and give implementations of the operations.23 All operations should be accomplished in O(1) steps.
作者: win_hate    时间: 2008-12-20 12:03
标题: Exercise 3.23. 答案
前面的队列使用了一个单链表:


改成双向的。



  1. (define (front-ptr deque) (car deque))

  2. (define (rear-ptr deque) (cdr deque))

  3. (define (set-front-ptr! deque item)
  4.   (set-car! deque item))

  5. (define (set-rear-ptr! deque item)
  6.   (set-cdr! deque item))

  7. (define (empty-deque? deque)
  8.   (null? (front-ptr deque)))

  9. (define (make-deque) (cons '() '()))

  10. (define (front-deque deque)
  11.   (if (empty-deque? deque)
  12.       (error "FRONT called with an empty deque" deque)
  13.       (caar (front-ptr deque))))

  14. (define (rear-deque deque)
  15.   (if (empty-deque? deque)
  16.       (error "REAR called with an empty deque" deque)
  17.       (caar (rear-ptr deque))))

  18. (define (rear-insert-deque! deque item)
  19.   (let ((new-pair (cons (cons item '()) '())))
  20.     (cond ((empty-deque? deque)
  21.            (set-front-ptr! deque new-pair)
  22.            (set-rear-ptr! deque new-pair))
  23.           (else
  24.            (set-cdr! (rear-ptr deque) new-pair)
  25.            (set-cdr! (car new-pair) (rear-ptr deque))
  26.            (set-rear-ptr! deque new-pair)))))

  27. (define (front-insert-deque! deque item)
  28.   (let ((new-pair (cons (cons item '()) '())))
  29.     (cond ((empty-deque? deque)
  30.            (set-front-ptr! deque new-pair)
  31.            (set-rear-ptr! deque new-pair))
  32.           (else
  33.            (set-cdr! new-pair (front-ptr deque))
  34.            (set-cdr! (cadr new-pair) new-pair)
  35.            (set-front-ptr! deque new-pair)))))

  36. (define (print-deque deque)
  37.   (let ((rear (rear-ptr deque)))
  38.     (define (f xs)
  39.       (cond ((eq? xs rear)
  40.              (display (caar xs))
  41.              (display " "))
  42.             (else
  43.              (display (caar xs))
  44.              (display " ")
  45.              (f (cdr xs)))))
  46.     (if (empty-deque? deque)
  47.         (display "empty deque\n")
  48.         (f (front-ptr deque)))))

  49. (define (front-delete-deque! deque)
  50.   (cond ((empty-deque? deque)
  51.          (error "Delete called with empty deque" deque))
  52.         ((eq? (front-ptr deque)
  53.               (rear-ptr deque))
  54.          (set-front-ptr! deque '())
  55.          (set-rear-ptr! deque '()))
  56.         (else
  57.          (set-front-ptr! deque (cdr (front-ptr deque)))
  58.          (set-cdr! (car (front-ptr deque)) '()))))

  59. (define (rear-delete-deque! deque)
  60.   (cond ((empty-deque? deque)
  61.          (error "Delete called with empty deque" deque))
  62.         ((eq? (front-ptr deque)
  63.               (rear-ptr deque))
  64.          (set-front-ptr! deque '())
  65.          (set-rear-ptr! deque '()))
  66.         (else
  67.          (set-rear-ptr! deque (cdar (rear-ptr deque)))
  68.          (set-cdr! (rear-ptr deque) '()))))
复制代码


演示


  1. > (define dq (make-deque))

  2. > (empty-deque? dq)
  3. #t

  4. > (rear-insert-deque! dq 'a)
  5. > (empty-deque? dq)
  6. #f

  7. > (rear-insert-deque! dq 'b)
  8. > (rear-insert-deque! dq 'c)
  9. > (print-deque dq)
  10. a b c

  11. > (front-insert-deque! dq 'A)
  12. > (front-insert-deque! dq 'B)
  13. > (front-insert-deque! dq 'C)
  14. > (print-deque dq)
  15. C B A a b c

  16. > (rear-delete-deque! dq)
  17. > (print-deque dq)
  18. C B A a b

  19. > (front-delete-deque! dq)
  20. > (print-deque dq)
  21. B A a b
复制代码

作者: win_hate    时间: 2008-12-21 10:35
标题: Exercise 3.24.
Exercise 3.24.  In the table implementations above, the keys are tested for equality using equal? (called by assoc). This is not always the appropriate test. For instance, we might have a table with numeric keys in which we don't need an exact match to the number we're looking up, but only a number within some tolerance of it. Design a table constructor make-table that takes as an argument a same-key? procedure that will be used to test ``equality'' of keys. Make-table should return a dispatch procedure that can be used to access appropriate lookup and insert! procedures for a local table.
作者: win_hate    时间: 2008-12-21 10:42
标题: Exercise 3.24. 答案

  1. (define (make-table cmp)
  2.   (let ((local-table (list '*table*)))
  3.     (define (assoc key records cmp)
  4.       (cond ((null? records) #f)
  5.             ((cmp key (caar records)) (car records))
  6.             (else (assoc key (cdr records) cmp))))
  7.    
  8.     (define (lookup key1 key2)
  9.       (let ((subtable (assoc key1 (cdr local-table) cmp)))
  10.         (if subtable
  11.             (let ((record (assoc key2 (cdr subtable) cmp)))
  12.               (if record
  13.                   (cdr record)
  14.                   #f))
  15.             #f)))

  16.     (define (insert! key1 key2 value)
  17.       (let ((subtable (assoc key1 (cdr local-table) cmp)))
  18.         (if subtable
  19.             (let ((record (assoc key2 (cdr subtable) cmp)))
  20.               (if record
  21.                   (set-cdr! record value)
  22.                   (set-cdr! subtable
  23.                             (cons (cons key2 value)
  24.                                   (cdr subtable)))))
  25.             (set-cdr! local-table
  26.                       (cons (list key1
  27.                                   (cons key2 value))
  28.                             (cdr local-table)))))
  29.         'ok)

  30.       (define (dispatch m)
  31.         (cond ((eq? m 'lookup-proc) lookup)
  32.               ((eq? m 'insert-proc!) insert!)
  33.               (else (error "Unknown operation -- TABLE" m))))
  34.       dispatch))
  35.             

  36. (define (cmp x y)
  37.   (< (abs (- x y)) 0.5))
复制代码


演示


  1. > (define tb (make-table cmp))
  2. > (define get (tb 'lookup-proc))
  3. > (define put (tb 'insert-proc!))

  4. > (get 1.2 2.3)
  5. #f

  6. > (put 1 2 3)
  7. ok

  8. > (get 1.2 2.3)
  9. 3
复制代码

作者: win_hate    时间: 2008-12-21 10:44
标题: Exercise 3.25.
Exercise 3.25.  Generalizing one- and two-dimensional tables, show how to implement a table in which values are stored under an arbitrary number of keys and different values may be stored under different numbers of keys. The lookup and insert! procedures should take as input a list of keys used to access the table.
作者: win_hate    时间: 2008-12-21 11:23
标题: Exercise 3.25. 答案
思路





  1. (define (make-table)
  2.   (list '*table*))

  3. (define (assoc key records)
  4.   (cond ((null? records) #f)
  5.         ((equal? key (caar records)) (car records))
  6.         (else (assoc key (cdr records)))))

  7. (define (locate keys table)
  8.   (define (find keys records)
  9.     (if  (null? keys) (cons '() records)
  10.          (let ((subtable (assoc (car keys) (cdr records))))
  11.            (if (eq? subtable #f)
  12.                (cons keys records)
  13.                (find (cdr keys) subtable)))))

  14.   (find keys table))

  15. (define (lookup keys table)
  16.   (let ((tmp (locate (append keys '(())) table)))
  17.     (if (null? (car tmp))
  18.         (cddr tmp)
  19.         #f)))
  20.   
  21. (define (insert! keys value table)  
  22.   (define (create keys)
  23.     (if (null? (cdr keys))
  24.         (cons (car keys) value)
  25.         (list (car keys) (create (cdr keys)))))
  26.   
  27.   (let* ((tmp (locate (append keys '(())) table))
  28.          (rest-keys (car tmp))
  29.          (subtable (cdr tmp)))
  30.     (cond ((null? rest-keys) (set-cdr! subtable))
  31.           (else
  32.            (set-cdr! subtable
  33.                      (cons (create rest-keys) (cdr subtable)))))))
复制代码


演示


  1. > (define tb (make-table))

  2. > (lookup '(a b) tb)
  3. #f

  4. > (insert! '(a b) 1 tb)
  5. > (lookup '(a b) tb)
  6. 1

  7. > (insert! '(a b c) 2 tb)
  8. > (lookup '(a b c) tb)
  9. 2
  10. > (lookup '(a b) tb)
  11. 1

  12. > (insert! '(a) 1 tb)
  13. > (lookup '(a) tb)
  14. 1
  15. > (lookup '(a b) tb)
  16. 1
  17. > (lookup '(a b c) tb)
  18. 2

  19. > (insert! '(d e f g h) 1000 tb)
  20. > (lookup '(d e f g h) tb)
  21. 1000
  22. > (lookup '(d e f) tb)
  23. #f
复制代码

[ 本帖最后由 win_hate 于 2008-12-21 11:25 编辑 ]
作者: win_hate    时间: 2008-12-22 17:52
标题: Exercise 3.26.
Exercise 3.26.  To search a table as implemented above, one needs to scan through the list of records. This is basically the unordered list representation of section 2.3.3. For large tables, it may be more efficient to structure the table in a different manner. Describe a table implementation where the (key, value) records are organized using a binary tree, assuming that keys can be ordered in some way (e.g., numerically or alphabetically). (Compare exercise 2.66 of chapter 2.)
作者: win_hate    时间: 2008-12-22 17:53
没兴趣,这个题目以后做。

后面的一些题目要有趣得多。
作者: win_hate    时间: 2008-12-22 17:54
标题: Exercise 2.27.
Exercise 3.27.  Memoization (also called tabulation) is a technique that enables a procedure to record, in a local table, values that have previously been computed. This technique can make a vast difference in the performance of a program. A memoized procedure maintains a table in which values of previous calls are stored using as keys the arguments that produced the values. When the memoized procedure is asked to compute a value, it first checks the table to see if the value is already there and, if so, just returns that value. Otherwise, it computes the new value in the ordinary way and stores this in the table. As an example of memoization, recall from section 1.2.2 the exponential process for computing Fibonacci numbers:

(define (fib n)
  (cond ((= n 0) 0)
        ((= n 1) 1)
        (else (+ (fib (- n 1))
                 (fib (- n 2))))))

The memoized version of the same procedure is

(define memo-fib
  (memoize (lambda (n)
             (cond ((= n 0) 0)
                   ((= n 1) 1)
                   (else (+ (memo-fib (- n 1))
                            (memo-fib (- n 2))))))))

where the memoizer is defined as

(define (memoize f)
  (let ((table (make-table)))
    (lambda (x)
      (let ((previously-computed-result (lookup x table)))
        (or previously-computed-result
            (let ((result (f x)))
              (insert! x result table)
              result))))))

Draw an environment diagram to analyze the computation of (memo-fib 3). Explain why memo-fib computes the nth Fibonacci number in a number of steps proportional to n. Would the scheme still work if we had simply defined memo-fib to be (memoize fib)?
作者: win_hate    时间: 2008-12-22 18:20
标题: Exercise 3.27. 答案
(memoize f) 返回一个函数,这个函数的参数为自然数。如果 n 对应的函数值(f(n)) 已经在局部表中,则使用表格中的值,避免了计算;如果 n 对应的函数值不存在于表格中,则计算 f(n) 把结果插入到表格中。

f 必须能与 memoize 配合才能提高效率,简单地 (memoize fib) 是不行的。比如


  1. (define g (memoize fib))
复制代码


假定现在表格中存有 1~n 对应的 fib(n),在计算 g(n+1) 时,还是会通过 fib (n+1) 来计算, fib 对表格一无所知,不断通过自身进行递归,重新计算出表格中已有的数据。

题目的方法比较 'hack',



这样实际上造出了 memo-fib 通过自身的递归,而它会查找、修改局部表。

下面是我写的带记忆的 fib 函数,比题目的容易理解多了。


  1. (define (make-fib)
  2.   (let ((table (make-table)))
  3.     (define (fib n)
  4.       (let ((t1 (lookup n table)))
  5.         (if t1 t1
  6.             (let ((t2 (+ (fib (- n 1))
  7.                         (fib (- n 2)))))
  8.               (insert! n t2 table)
  9.               t2))))
  10.     (insert! 0 0 table)
  11.     (insert! 1 1 table)
  12.     fib))
复制代码


这个题目有个有趣的地方,书上没提到---这个实现可以计算多大的 fib 值?


  1. > (memo-fib 250)
  2. ERROR: Stack overflow
  3. ABORT: (stack-overflow)
复制代码


oops, 250 项都算不出来。但记忆表已经避免了很多递归啊。

避免递归的前提是记忆表中有足够多的数据。按现在的写法,如果表格是空的,或者只有少量数据,仍可能产生很深的递归。

这样一分析,可以看出,计算大的 n 是可行的,只要先计算一些小的 n.

再来:


  1. > (memo-fib 200)
  2. 280571172992510140037611932413038677189525

  3. > (memo-fib 400)
  4. 176023680645013966468226945392411250770384383304492191886725992896575345044216019675

  5. > (memo-fib 600)
  6. 110433070572952242346432246767718285942590237357555606380008891875277701705731473925618404421867819924194229142447517901959200

  7. > (memo-fib 800)
  8. 69283081864224717136290077681328518273399124385204820718966040597691435587278383112277161967532530675374170857404743017623467220361778016172106855838975759985190398725

  9. > (memo-fib 1000)
  10. 43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875

  11. > (memo-fib 1500)
  12. ERROR: Stack overflow
  13. ABORT: (stack-overflow)

  14. > (memo-fib 1200)
  15. 27269884455406270157991615313642198705000779992917725821180502894974726476373026809482509284562310031170172380127627214493597616743856443016039972205847405917634660750474914561879656763268658528092195715626073248224067794253809132219056382939163918400

  16. > (memo-fib 1500)
  17. ERROR: Stack overflow
  18. ABORT: (stack-overflow)

  19. > (memo-fib 1400)
  20. 17108476902340227241249719513231821477382749898026920041550883749834348017250935801359315038923367841494936038231522506358371361016671790887791259870264957823133253627917432203111969704623229384763490617075388642696139893354058660570399927047816296952516330636633851111646387885472698683607925

  21. > (memo-fib 1600)
  22. 10733451489189611103121609043038710477166925241925645413424099370355605456852169736033991876014762808340865848447476173426115162172818890323837138136782951865054538417494035229785971002587932638902311416018904156170269354720460896363558168129004231138415225204738582550720791061581463934092726107458349298577292984375276210232582438075
复制代码


最后,如果初始表是空的,计算 (memo-fib n) 的递归深度是 n,‘宽度’ 是 2,所以是 O(n) 的。

[ 本帖最后由 win_hate 于 2008-12-22 18:39 编辑 ]
作者: win_hate    时间: 2008-12-26 23:25
标题: Exercise 3.28.
Exercise 3.28.  Define an or-gate as a primitive function box. Your or-gate constructor should be similar to and-gate.
作者: win_hate    时间: 2008-12-26 23:27
标题: Exercise 3.28. 答案

  1. (define (or-gate a1 a2 output)
  2.   (define (or-action-procedure)
  3.     (let ((new-value
  4.            (logical-or (get-signal a1) (get-signal a2))))
  5.       (after-delay or-gate-delay
  6.                    (lambda ()
  7.                      (set-signal! output new-value)))))
  8.   (add-action! a1 or-action-procedure)
  9.   (add-action! a2 or-action-procedure)
  10.   'ok)
复制代码


演示


  1. (define a (make-wire))
  2. (define b (make-wire))
  3. (define c (make-wire))
  4. (or-gate a b c)
  5. (set-signal! a 1)
  6. (set-signal! b 0)
  7. (probe 'a a)
  8. (probe 'b b)
  9. (probe 'c c)
  10. (propagate)
复制代码


输出:


  1. a 0 New-value = 1
  2. b 0 New-value = 0
  3. c 0 New-value = 0
  4. c 5 New-value = 1
复制代码


注意这里的代码需要整个电路包,必须把整节代码完成后,才能运行。

[ 本帖最后由 win_hate 于 2008-12-26 23:40 编辑 ]
作者: win_hate    时间: 2008-12-26 23:27
标题: Exercise 3.29.
Exercise 3.29.  Another way to construct an or-gate is as a compound digital logic device, built from and-gates and inverters. Define a procedure or-gate that accomplishes this. What is the delay time of the or-gate in terms of and-gate-delay and inverter-delay?
作者: win_hate    时间: 2008-12-26 23:28
标题: Exercise 3.29. 答案

  1. (define (my-or-gate a1 a2 output)
  2.   (let ((b1 (make-wire))
  3.         (b2 (make-wire))
  4.         (c (make-wire)))
  5.     (inverter a1 b1)
  6.     (inverter a2 b2)
  7.     (and-gate b1 b2 c)
  8.     (inverter c output)
  9.   'ok))
复制代码


演示


  1. (define a (make-wire))
  2. define b (make-wire))
  3. (define c (make-wire))
  4. (my-or-gate a b c)
  5. (set-signal! a 1)
  6. (set-signal! b 0)
  7. (probe 'a a)
  8. (probe 'b b)
  9. (probe 'c c)
  10. (propagate)
复制代码


  1. a 0 New-value = 1
  2. b 0 New-value = 0
  3. c 0 New-value = 0
  4. c 2 New-value = 1
  5. c 7 New-value = 0
  6. c 7 New-value = 1
复制代码


注意 c 在时刻 2 变成了 1。 这是因为 make-wire 得到的线初始值为 0,所以整个系统一般并不处于稳定状态。比如一开始,内部的 c 和外部的 c(接口中的 output) 都是 0,所以在时刻 2,外部的 c 变为 1。

另外,这个 or-gate 不是原生的,而是利用 inverter 和 and-gate 搭起来的,所以延时跟 3.28 中的不一样。3.28 中那个延时是指定的。

[ 本帖最后由 win_hate 于 2008-12-27 16:19 编辑 ]
作者: win_hate    时间: 2008-12-27 16:14
标题: Exercise 3.30.
Exercise 3.30.  Figure 3.27 shows a ripple-carry adder formed by stringing together n full-adders. This is the simplest form of parallel adder for adding two n-bit binary numbers. The inputs A1, A2, A3, ..., An and B1, B2, B3, ..., Bn are the two binary numbers to be added (each Ak and Bk is a 0 or a 1). The circuit generates S1, S2, S3, ..., Sn, the n bits of the sum, and C, the carry from the addition. Write a procedure ripple-carry-adder that generates this circuit. The procedure should take as arguments three lists of n wires each -- the Ak, the Bk, and the Sk -- and also another wire C. The major drawback of the ripple-carry adder is the need to wait for the carry signals to propagate. What is the delay needed to obtain the complete output from an n-bit ripple-carry adder, expressed in terms of the delays for and-gates, or-gates, and inverters?


Figure 3.27:  A ripple-carry adder for n-bit numbers.
作者: win_hate    时间: 2008-12-27 16:17
标题: Exercise 3.30. 答案 (上)

  1. (define (full-adder A B Ci S Co)
  2.   (let ((tmp (make-wire))
  3.         (or1 (make-wire))
  4.         (or2 (make-wire)))
  5.     (half-adder B Ci tmp or2)
  6.     (half-adder A tmp S or1)
  7.     (or-gate or1 or2 Co)
  8.     'ok))
  9.         
  10. (define (ripple-carry-adder A B S C)
  11.   (define (f A B S last-c)
  12.     (if (null? (cdr A))
  13.         (full-adder (car A) (car B) last-c
  14.                     (car S) C)
  15.         (let ((c (make-wire)))
  16.           (full-adder (car A) (car B) last-c
  17.                       (car S) c)
  18.           (f (cdr A) (cdr B) (cdr S) c))))
  19.   (let ((c (make-wire)))
  20.     (f A B S c)))

  21. (define (make-wires n)
  22.   (if (= n 0) '()
  23.       (cons (make-wire) (make-wires (- n 1)))))

  24. (define (set-signals! ws vs)
  25.   (cond ((not (null? ws))
  26.          (set-signal! (car ws) (car vs))
  27.          (set-signals! (cdr ws) (cdr vs)))))

  28. (define (get-signals ws)
  29.   (if (null? ws) '()
  30.       (cons (get-signal (car ws))
  31.             (get-signals (cdr ws)))))

  32. (define (probe-signals ws symb n)
  33.   (cond ((null? ws) 'ok)
  34.         (else
  35.          (probe (cons n symb) (car ws))
  36.          (probe-signal (cdr ws) symb (- n 1)))))
复制代码


演示代码:


  1. (define A (make-wires 7))
  2. (define B (make-wires 7))
  3. (define S (make-wires 7))
  4. (define c (make-wire))
  5. (set-signals! A '(1 1 1 1 1 1 1))
  6. (set-signals! B '(1 1 1 1 1 1 1))
  7. (ripple-carry-adder A B S c)
复制代码


演示输出:


  1. guile> (propagate)
  2. done
  3. guile> (get-signals A)
  4. (1 1 1 1 1 1 1)
  5. guile> (get-signals B)
  6. (1 1 1 1 1 1 1)
  7. guile> (get-signals S)
  8. (0 1 1 1 1 1 1)
  9. guile> (get-signal c)
  10. 1
复制代码

[ 本帖最后由 win_hate 于 2008-12-27 16:26 编辑 ]
作者: win_hate    时间: 2008-12-27 16:23
标题: Exercise 3.30. 答案 (中)
现在讨论延时。把或门,与门,反转器的延迟记为 or, and 和 inv.



按书本的设置:or=5, and=3, inv=2

有 dhs=max(or, and+inv)+and=max(5, 5)+3=8
dhc=and=3

dfs = 2dhs = 16
dfc = dhs+dhc+or=8+3+5=16

所以 C 的延迟为 16n, S1 的延迟为 16(n-1)+16=16n

这个只是理论上的最大延迟,但实际未必会达到。比如两个数相加,如果根本没有进位,就不会有进位带来的延迟,大小为 16 的时延就足够了。

[ 本帖最后由 win_hate 于 2008-12-27 16:27 编辑 ]
作者: win_hate    时间: 2008-12-27 16:57
标题: Exercise 3.30. 答案 (下)
下面我们看一个具体的例子。用 probe 函数测试实际延迟,并手工计算延迟进行对比。

为简明起见,这里 ripple-carry-adder 的输入设置为两个比特。前面提到过,初始系统是不稳定的,分析起来很罗唆。所以我先用 (0 0) 跟 (1 1) 加一次。最后的结果是稳定的。此时设置时刻为 0,然后把 (0 0) 改为 (1 0),这样将从最低位引发一个进位,并传播到最终进位上。

代码


  1. (define A (make-wires 2))
  2. (define B (make-wires 2))
  3. (define S (make-wires 2))
  4. (define c (make-wire))
  5. (set-signals! A '(0 0))
  6. (set-signals! B '(1 1))


  7. (ripple-carry-adder A B S c)
  8. (propagate)

  9. (set-car! the-agenda 0)

  10. (set-signals! A '(1 0))
  11. (probe 'c c)
  12. (probe 's1 (cadr S))
  13. (propagate)
复制代码


输出:


  1. c 0 New-value = 0
  2. s1 0 New-value = 1

  3. c 16 New-value = 1
  4. s1 24 New-value = 0
复制代码


前两行是第一次 propagate 后 c, s1 的状态。此时系统是稳定的。

把 A 设置为 (1 0) 后,计算结果为 (1 0) + (1 1) = (0 0 1)  ;; 左边是最低位

c 在时刻 16 稳定为 1, 而 s1 在时刻 24 稳定为 0



c 和 s1 见上面的图。由于此时是两比特加两比特,所以上图中 n 为 2,s2 是最低位。

在时刻 0, A2 变为 1,看下面的半加器和全加器。



半加器的 A 在时刻 0 变为 1,所以 C 在时刻 3 改变, S 在时刻 8 改变。后面会看到,S 的改变对我们的分析没有影响。

再看全加器,现在是全加器中上面那个半加器的输出改变了。我们只关心这个全加器的 C(out) ,它在 5 个时刻后,也就是时刻 8 它的值从 0 变为 1。

于是我们看到,在时刻 8, 全加器 s2 产生的进位输出变为 1。

再看下面的半加器和全加器:



半加器的输入 C(in)  在时刻 8 从 0 变为 1。于是在 3 个时刻后,or-gate 靠下方的输入变为 1,5 个时刻后 or-gate 的输出 C(out) 变为 1。
由于 or-gate 有一个输入为 1,所以输出永远为 1,从而其输出稳定下来。这个时刻为 8+3+5=16

那么 SUM 呢? 在时刻 8, C(in) 从 0 变为 1, 在 8 个时刻后,全加器中靠上的半加器的下方输入发生改变,又过 8 个时刻,SUM 发生改变。稳定时刻为 8+8+8 = 24.

这个分析与实际运行是一致的。

[ 本帖最后由 win_hate 于 2008-12-27 17:54 编辑 ]
作者: win_hate    时间: 2008-12-28 14:36
标题: Exercise 3.31.
Exercise 3.31.   The internal procedure accept-action-procedure! defined in make-wire specifies that when a new action procedure is added to a wire, the procedure is immediately run. Explain why this initialization is necessary. In particular, trace through the half-adder example in the paragraphs above and say how the system's response would differ if we had defined accept-action-procedure! as

(define (accept-action-procedure! proc)
  (set! action-procedures (cons proc action-procedures)))
作者: win_hate    时间: 2008-12-28 15:36
标题: Exercise 3.31. 答案
如果在 accept-action-procedure! 时不马上把函数执行一次(其实是插入 the-agenda 表),则这个函数只被放进了线(wire)的函数列表,没有被放到 the-agenda 中。直到此线上信号改变时,这些函数才被插入到 the-agenda 中,排期执行。这会使整个系统中存在错误状态。以最简单的 inverter 为例,它对应的代码为:


  1. (define (inverter input output)
  2.   (define (invert-input)
  3.     (let ((new-value (logical-not (get-signal input))))
  4.       (after-delay inverter-delay
  5.                    (lambda ()
  6.                      (set-signal! output new-value)))))
  7.   (add-action! input invert-input)
  8.   'ok)
复制代码


线的初始值都是 0, 那么这个 inverter 的输入、输出都是 0,显然是不对的。如果 add-actions! 的时候把 invert-input 放到 the-agenda 中排队,那么两个时刻后,这个 inverter 的输入、输出就合乎逻辑了。如果 add-actions! 的时候,invert-input 没有马上排期,则 0 反转为 0 的错误状态将一直保持,直到这个 inverter 的输入发生变化才能更正。

书本上半加器的例子:


  1. (define input-1 (make-wire))
  2. (define input-2 (make-wire))
  3. (define sum (make-wire))
  4. (define carry (make-wire))
  5. (probe 'sum sum)
  6. sum 0  New-value = 0
  7. (probe 'carry carry)
  8. carry 0  New-value = 0

  9. (half-adder input-1 input-2 sum carry)
  10. ok
  11. (set-signal! input-1 1)
  12. done
  13. (propagate)
  14. sum 8  New-value = 1
  15. done

  16. (set-signal! input-2 1)
  17. done
  18. (propagate)
  19. carry 11  New-value = 1
  20. sum 16  New-value = 0
  21. done
复制代码


象本题那样把代码改过后,书本上的半加器例子的输出变为:


  1. (define input-1 (make-wire))
  2. (define input-2 (make-wire))
  3. (define sum (make-wire))
  4. (define carry (make-wire))

  5. (probe 'sum sum)

  6. (probe 'carry carry)
复制代码

probe 的时候没有输出,因为对应的函数没有被马上执行。



  1. > (half-adder input-1 input-2 sum carry)
  2. ok
  3. > (set-signal! input-1 1)
  4. > (propagate)
  5. done
复制代码


现在半加器的输入是 1, 0,输出是 0,0。这显然是错的。看看下面的半加器



A 变为 1, 这导致最右边的 and-gate 的靠上方的输入为 1,但靠下方的输入呢? 左边 and-gete 的第一个输入改变为1,导致此 and-gate 的输出我为 0。但由于这个输出初始为 0,所以没有变化。这使得 inverter 的输入没有变化,从而输出也不变!如果当初代码没改动,则 inverter 的输入是会改变的。最终 S 得到一个错误的输出 0.


  1. > (set-signal! input-2 1)
  2. > (propagate)
  3. carry 11 New-value = 1
  4. done
复制代码


把半加器的 B 也设置为 1,此时 inverter 线上的错误被更正,得到正确的输出 S=0, C=1。但 S 的值没有变化,所以没有相应的 probe 信息。

[ 本帖最后由 win_hate 于 2008-12-29 23:16 编辑 ]
作者: win_hate    时间: 2008-12-29 23:16
标题: Exercise 3.32.
Exercise 3.32.  The procedures to be run during each time segment of the agenda are kept in a queue. Thus, the procedures for each segment are called in the order in which they were added to the agenda (first in, first out). Explain why this order must be used. In particular, trace the behavior of an and-gate whose inputs change from 0,1 to 1,0 in the same segment and say how the behavior would differ if we stored a segment's procedures in an ordinary list, adding and removing procedures only at the front (last in, first out).
作者: win_hate    时间: 2008-12-29 23:22
标题: Exercise 3.32. 答案
先进先出使得电路上信号的变化与实际相符合,如果后进先出,会得到一个不同的信号序列。

比如 and 门输入从 0, 1 变为 1,0。尽管是同时发生,但仍有一个先后次序。如果先 0->1 再  1->0,则:

先入先出产生的输出信号为: 0(0), 1(3), 0(3)              ;;括号中的是时刻

如果是后进先出产生的输出信号为:0(0), 0(3)

[ 本帖最后由 win_hate 于 2008-12-29 23:34 编辑 ]
作者: win_hate    时间: 2009-01-01 11:49
标题: Exercise 3.33.
Exercise 3.33.  Using primitive multiplier, adder, and constant constraints, define a procedure averager that takes three connectors a, b, and c as inputs and establishes the constraint that the value of c is the average of the values of a and b.
作者: win_hate    时间: 2009-01-01 11:51
标题: Exercise 3.33. 答案 (上)

  1. (define a (make-connector))
  2. (define b (make-connector))
  3. (define c (make-connector))

  4. (define (average a b c)
  5.   (let ((f (make-connector))
  6.         (u (make-connector)))
  7.     (adder a b u)
  8.     (constant 2 f)
  9.     (multiplier c f u)
  10.     'ok))

  11. (average a b c)
  12. (probe 'c c)
  13. (probe 'a a)
  14. (probe 'b b)
复制代码


演示


  1. > (set-value! a 10 'zxl)

  2. Probe: a = 10done
  3. > (set-value! b 20 'win_hate)

  4. Probe: b = 20
  5. Probe: c = 15done
  6. > (forget-value! b 'zxl)
  7. ignored
  8. > (forget-value! b 'win_hate)

  9. Probe: b = ?
  10. Probe: c = ?done
  11. > (set-value! c 20 'zxl)

  12. Probe: c = 20
  13. Probe: b = 30done
复制代码

[ 本帖最后由 win_hate 于 2009-1-1 11:54 编辑 ]
作者: win_hate    时间: 2009-01-01 12:11
标题: Exercise 3.33. 答案 (下)
对 Probe 时机的讨论。

如果我们把开始的代码改为:


  1. (define a (make-connector))
  2. (define b (make-connector))
  3. (define c (make-connector))

  4. (define (average a b c)
  5.   (let ((f (make-connector))
  6.         (u (make-connector)))
  7.     (adder a b u)
  8.     (constant 2 f)
  9.     (multiplier c f u)
  10.     'ok))

  11. (probe 'b b)
  12. (average a b c)
  13. (probe 'c c)
  14. (probe 'a a)
复制代码


不同之处仅在于 (probe 'b b) 一句提到 (average a b c) 前面了。运行结果变为


  1. guile> (load "3.33.scm")
  2. guile> (set-value! a 10 'zxl)

  3. Probe: a = 10done
  4. guile> (set-value! b 20 'win_hate)

  5. Probe: c = 15
  6. Probe: b = 20done
  7. guile> (forget-value! b 'win_hate)

  8. Probe: c = ?
  9. Probe: b = ?done
复制代码


看上去是先 forget c ,然后才 forget b 的。但我们给出的指令是让 b forget,它 forget 后再通知周边约束,使 forget 指令传播出去。所以现在的输出有点可疑。

仔细分析一下代码,就会发现,连接器把约束存放在一个列表里,使用的方法是后进先出。Probe 也是一个约束器件:



[ 本帖最后由 win_hate 于 2009-1-2 11:08 编辑 ]
作者: win_hate    时间: 2009-01-02 11:02
标题: Exercise 3.34.
Exercise 3.34.  Louis Reasoner wants to build a squarer, a constraint device with two terminals such that the value of connector b on the second terminal will always be the square of the value a on the first terminal. He proposes the following simple device made from a multiplier:

(define (squarer a b)
  (multiplier a a b))

There is a serious flaw in this idea. Explain.
作者: win_hate    时间: 2009-01-02 11:08
标题: Exercise 3.34. 答案
乘法器的三个连接器中,有两个确定了,才能求出第三个。

这个 squarer 的实现中,本质上只有两个连接器。在 a 给定时,三个连接器中有两个有值,所以能求出 b。但 b 给定时,另外‘两’ 个连接器 a 没有值。按乘法器的设计,只有一个值是无法求出另外两个值的。
作者: win_hate    时间: 2009-01-03 10:26
标题: Exercise 3.35.
Exercise 3.35.  Ben Bitdiddle tells Louis that one way to avoid the trouble in exercise 3.34 is to define a squarer as a new primitive constraint. Fill in the missing portions in Ben's outline for a procedure to implement such a constraint:

(define (squarer a b)
  (define (process-new-value)
    (if (has-value? b)
        (if (< (get-value b) 0)
            (error "square less than 0 -- SQUARER" (get-value b))
            <alternative1>)
        <alternative2>))
  (define (process-forget-value) <body1>)
  (define (me request) <body2>)
  <rest of definition>
  me)
作者: win_hate    时间: 2009-01-03 10:30
标题: Exercise 3.35. 答案

  1. (define (squarer a b)
  2.   (define (process-new-value)
  3.     (if (has-value? b)
  4.         (if (< (get-value b) 0)
  5.             (error "square less than 0 -- SQUARER" (get-value b))
  6.             (set-value! a (sqrt (get-value b)) me))
  7.         (if (has-value? a)
  8.             (set-value! b (* (get-value a) (get-value a)) me))))
  9.     (define (process-forget-value)
  10.       (forget-value! a me)
  11.       (forget-value! b me)
  12.       (process-new-value))
  13.     (define (me request)
  14.       (cond ((eq? request 'I-have-a-value)
  15.              (process-new-value))
  16.             ((eq? request 'I-lost-my-value)
  17.              (process-forget-value))
  18.             (else
  19.              (error "Unknown request -- SQUARER" request))))
  20.     (connect a me)
  21.     (connect b me)
  22.     me)
复制代码


演示使用的代码


  1. (load "3.35.scm")

  2. (define a (make-connector))
  3. (define b (make-connector))


  4. (squarer a b)
  5. (probe 'a a)
  6. (probe 'b b)
复制代码


交互环境中的演示:


  1. > (load "test.scm")
  2. > (set-value! a 100 'zxl)

  3. Probe: a = 100
  4. Probe: b = 10000done
  5. > (forget-value! a 'zxl)

  6. Probe: a = ?
  7. Probe: b = ?done
  8. > (set-value! b 100 'zxl)

  9. Probe: b = 100
  10. Probe: a = 10.0done
  11. > (forget-value! b 'zxl)

  12. Probe: b = ?
  13. Probe: a = ?done
  14. > (set-value! a -10 'zxl)

  15. Probe: a = -10
  16. Probe: b = 100done
复制代码

作者: win_hate    时间: 2009-01-04 11:43
标题: Exercise 3.36.
Exercise 3.36.  Suppose we evaluate the following sequence of expressions in the global environment:

(define a (make-connector))
(define b (make-connector))
(set-value! a 10 'user)

At some time during evaluation of the set-value!, the following expression from the connector's local procedure is evaluated:

(for-each-except setter inform-about-value constraints)

Draw an environment diagram showing the environment in which the above expression is evaluated.
作者: win_hate    时间: 2009-01-04 11:43
标题: Exercise 3.36. 答案
<wait for pic>
作者: win_hate    时间: 2009-01-04 11:44
标题: Exercise 3.37.
Exercise 3.37.  The celsius-fahrenheit-converter procedure is cumbersome when compared with a more expression-oriented style of definition, such as

(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))

Here c+, c*, etc. are the ``constraint'' versions of the arithmetic operations. For example, c+ takes two connectors as arguments and returns a connector that is related to these by an adder constraint:

(define (c+ x y)
  (let ((z (make-connector)))
    (adder x y z)
    z))

Define analogous procedures c-, c*, c/, and cv (constant value) that enable us to define compound constraints as in the converter example above.
作者: win_hate    时间: 2009-01-04 11:44
标题: Exercise 3.37. 答案

  1. (define (c+ x y)
  2.   (let ((z (make-connector)))
  3.     (adder x y z)
  4.     z))

  5. (define (c* x y)
  6.   (let ((z (make-connector)))
  7.     (multiplier x y z)
  8.     z))

  9. (define (c/ x y)
  10.   (let ((z (make-connector)))
  11.     (multiplier z y x)
  12.     z))

  13. (define (cv c)
  14.   (let ((x (make-connector)))
  15.     (constant c x)
  16.     x))

  17. (define (celsius-fahrenheit-converter x)
  18.   (c+ (c* (c/ (cv 9) (cv 5))
  19.           x)
  20.       (cv 32)))

  21. (define C (make-connector))
  22. (define F (celsius-fahrenheit-converter C))
  23. (probe 'C C)
  24. (probe 'F F)
复制代码

作者: win_hate    时间: 2009-01-05 11:47
本节完。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2