免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: win_hate
打印 上一主题 下一主题

SICP 习题参考答案 3.3 [复制链接]

论坛徽章:
0
21 [报告]
发表于 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)
复制代码

论坛徽章:
0
22 [报告]
发表于 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.

论坛徽章:
0
23 [报告]
发表于 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)
复制代码

论坛徽章:
0
24 [报告]
发表于 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.

论坛徽章:
0
25 [报告]
发表于 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
复制代码

论坛徽章:
0
26 [报告]
发表于 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.

论坛徽章:
0
27 [报告]
发表于 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
复制代码

论坛徽章:
0
28 [报告]
发表于 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.

论坛徽章:
0
29 [报告]
发表于 2008-12-21 11:23 |只看该作者

Exercise 3.25. 答案

思路


  • 多重键值的处理

    一个直接的思路是递归处理键值。例如,'a, 'b, 'c 键值对应 1,则 'a 键值下有一个 assoc 表,其中一项键值为 'b,其下又有一个 assoc 表,里头有一项为 (c . 1).

    当插入键值时,首先 lookup,如果失败,这创建要插入的内容,插入到合适的位置上。由于是多重键值,比如 ('a 'b 'c),lookup 失败可能发生在 'a,也可能发生在 'b,也可能发生在 'c,所示失败时只返回一个 #f 是不够的。lookup 在失败时的返回值应该包括失败标志,剩余的 key,插入位置。但这样的接口又使得 lookup 很难用。为此,我添加了一个叫 locate 的函数来处理这些繁琐的事情,而 insert!, lookup 都调用 locate,从而能保持简洁的接口。

  • 不同长度键值带来的问题

    但题目要求,两个值可以有不同长度的键列。比如 ('a) ->1, ('a 'b) -> 2。这样会带来一个问题。如果表中包含前者,则键 'a 对应的值为 1,即

    ('a . 1)

    但如果是后者,则 'a 对应的值是一个 assoc list,在这个 list 中,有一项 ('b . 1)。

    这两种情况是互斥的,会相互覆盖。如果先插入  'a -> 1,则再插入 ('a 'b)->2 时,前一个对应就没有了;反过来也一样。

    我采用的方案为:在给定键列尾部附加上一个伪键值 '(),用户的键值序列不允许包含此值。假如要插入 'a ->1 和 ('a 'b) ->2。在采用新方案前,两个键值序列为

    'a 和

    'a 'b

    当插入后面的 'b 时,新插入的 assoc list 会覆盖 'a 右边的值。

    采用新方案后,键值序列变为:

    'a () 和

    'a 'b '()

    当插入 'b 时,'a 的右边是个 assoc 表,把 ('b . ...) 插入这个 assoc 即可,不会产生覆盖。

    假设当前为空表,那么,当插入 'a->1 时,实际效果为

    ( a . ( () . 1)) ,

    再插入 ('a 'b) -> 2 时,变为

    (a . ( (b (() . 2 )) (() . 1))) .

    这个处理使得用户键值右边总是一个 assoc 表,从而简化了逻辑,避免了相互覆盖。




  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 编辑 ]

论坛徽章:
0
30 [报告]
发表于 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.)
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP