如何解决我实施 SICP Ex 2.85 时出现的奇怪错误?

How to resolve weird Bug in my implementation of SICP Ex 2.85?

练习要求我们制作一个简化数字的过程drop。例如Complex to Real(如果可能)然后修改现有的 apply-generic 以便它可以在计算后简化数字。

只需添加 drop 即可,并且在相同条件下它确实可以在过程之外工作。但是当添加到 apply-generic.

时它给了我一个错误

完整代码如下:

(define (attach-tag type-tag contents)
  (cons type-tag contents))

(define (type-tag datum)
  (if (pair? datum)
      (car datum)
      (error "Bad tagged datum: TYPE-TAG" datum)))

(define (contents datum)
  (if (pair? datum)
    (cdr datum)
    (error "Bad tagged datum: CONTENTS" datum)))

(define (put-coercion type1 type2 function)
  (put type1 type2 function))

(define (get-coercion type1 type2)
  (get type1 type2))

(define (element? ls e)
  (cond ((null? ls)   #f)
        ((eq? (car ls) e)   #t)
        (else   (element? (cdr ls) e))))

(define (compose f g) (lambda (x) (f (g x))))
(define (double f) (compose f f)) 
(define (identity x) x)

(define (compose-n f n)
  (cond ((= n 0)  identity)
        ((even? n)  (compose-n (double f) (/ n 2)))
        (else  (compose f (compose-n f (- n 1))))))

(define tower (list (list 'integer 0)
                    (list 'real 1)
                    (list 'complex 2)))

(define (child? ti t1 tower)
  (define (iter t tow)
    (cond
       ((null? tow)  (error "Not located in Tower" t))
       ((eq? t (caar tow))  (cadar tow))
       (else  (iter t (cdr tow)))))
  (if (< (- (iter t1 tower) (iter ti tower)) 1) 
      #f
      (- (iter t1 tower) (iter ti tower))))

(define (ti->t1 ti t1)
  (if (child? ti t1 tower)
    (compose-n raise (child? ti t1 tower))
    (get-coercion ti t1)))

(define (map-ls proc-ls arg-ls)
  (if (null? proc-ls) 
    '()
    (cons ((car proc-ls) (car arg-ls))
          (map-ls (cdr proc-ls) (cdr arg-ls)))))

(define (iterator t op ty args)
  (if (null? t) 
    (error "No method for these types"
           (list op ty)) 
    (let ((getter (map
               (lambda (x) (ti->t1 x (car t))) ty)
             ))         
      (if (element? getter #f)
        (iterator (cdr t) op ty args)
        (let ((eq-tags 
                  (map type-tag (map-ls getter args))))
          (let ((proc (get op eq-tags)))
            (if proc 
              (apply proc (map contents
                             (map-ls getter args)))
              (iterator (cdr t) op ty args))))))))

(define (drop x)
  (if (or (eq? (type-tag x) 'integer)
          (not (project x))) 
     x 
     (drop (project x))))


(define (apply-generic op . args)
    (let ((type-tags (map type-tag args)))
      (let ((proc (get op type-tags)))
        (if proc
           (drop (apply proc (map contents args)))
           (if (element? (map
                   (lambda (x) (eq? x (car type-tags)))
                        type-tags) #f)
              (drop (iterator type-tags op type-tags args))
              (error "No method for these types"
                     (list op type-tags))
        )))))

(define (install-integer-package)
  ;;internal Procedures
  (define (add x y)(+ (contents x) (contents y)))
  (define (sub x y)(- (contents x) (contents y)))
  (define (mul x y)(* (contents x) (contents y)))
  (define (exp x y)(expt (contents x) (contents y)))
  (define (make x)  x)
  (define (equ? x y)(= (contents x) (contents y)))  
  ;;External Procedures
  (define (tag x)
    (attach-tag 'integer x))
  (put 'add '(integer integer)
       (lambda (x y) (tag (+ x y))))
  (put 'sub '(integer integer)
       (lambda (x y) (tag (- x y))))
  (put 'mul '(integer integer)
       (lambda (x y) (tag (* x y))))
  (put 'div '(integer integer)
       (lambda (x y) (make-rational x y)))
  (put 'make 'integer
       (lambda (x) (tag (make x))))
  (put 'equ? '(integer integer)
       (lambda (x y) (equ? x y)))
  (put '=zero? '(integer)
       (lambda (x) (= (contents x) 0)))
  (put 'exp '(integer integer)
     (lambda (x y) (tag (exp x y))))
  (put 'raise '(integer)
       (lambda (x) (make-real  (* x 1.0))))
  'done)

(define (install-real-package)
  ;;internal Procedures
  (define (add x y)(+ (contents x) (contents y)))
  (define (sub x y)(- (contents x) (contents y)))
  (define (mul x y)(* (contents x) (contents y)))
  (define (div x y)(/ (contents x) (contents y)))
  (define (exp x y)(expt (contents x) (contents y)))
  (define (make x)  x)
  (define (equ? x y)(= (contents x) (contents y)))  
  ;;External Procedures
  (define (tag x)
    (attach-tag 'real x))
  (put 'add '(real real)
       (lambda (x y) (tag (add x y))))
  (put 'sub '(real real)
       (lambda (x y) (tag (sub x y))))
  (put 'mul '(real real)
       (lambda (x y) (tag (mul x y))))
  (put 'div '(real real)
       (lambda (x y) (tag (div x y))))
  (put 'make 'real
       (lambda (x) (tag (make x))))
  (put 'equ? '(real real)
       (lambda (x y) (equ? x y)))
  (put '=zero? '(real)
       (lambda (x) (= (contents x) 0)))
  (put 'exp '(real real)
     (lambda (x y) (tag (exp x y))))
  (put 'raise '(real)
       (lambda (x) (make-complex-from-real-imag
                     x 0)))
  (put 'project '(real)
       (lambda (x)(if (= (round x) x)
        (make-integer (round x))
         #f)))
  'done)

(define (install-rectangular-package)
  ;; internal procedures
  (define (real-part z) (car z))
  (define (imag-part z) (cdr z))
  (define (make-from-real-imag x y) 
    (cons x y))
  (define (magnitude z)
    (sqrt (+ (square (real-part z))
             (square (imag-part z)))))
  (define (angle z)
    (atan (imag-part z) (real-part z)))
  (define (make-from-mag-ang r a)
    (cons (* r (cos a)) (* r (sin a))))
  (define (equ? z1 z2)
    (and (= (real-part z1) (real-part z2))
         (= (real-part z1) (real-part z2))))
  ;; interface to the rest of the system
  (define (tag x) 
    (attach-tag 'rectangular x))
  (put 'real-part '(rectangular) real-part)
  (put 'imag-part '(rectangular) imag-part)
  (put 'magnitude '(rectangular) magnitude)
  (put 'angle '(rectangular) angle)
  (put 'make-from-real-imag 'rectangular
       (lambda (x y) 
         (tag (make-from-real-imag x y))))
  (put 'make-from-mag-ang 'rectangular
       (lambda (r a) 
         (tag (make-from-mag-ang r a))))
  (put 'equ? '(rectangular rectangular)
       (lambda (z1 z2) 
         (equ? z1 z2)))
  (put '=zero? '(rectangular)
       (lambda (x) (and (= (real-part x) 0)
                        (= (imag-part x) 0))))
  'done)

(define (install-polar-package)
  ;; internal procedures
  (define (magnitude z) (car z))
  (define (angle z) (cdr z))
  (define (make-from-mag-ang r a) (cons r a))
  (define (real-part z)
    (* (magnitude z) (cos (angle z))))
  (define (imag-part z)
    (* (magnitude z) (sin (angle z))))
  (define (make-from-real-imag x y)
    (cons (sqrt (+ (square x) (square y)))
          (atan y x)))
  (define (equ? z1 z2)
    (and (= (magnitude z1) (magnitude z2))
         (= (angle z1) (angle z2))))
  ;; interface to the rest of the system
  (define (tag x) (attach-tag 'polar x))
  (put 'real-part '(polar) real-part)
  (put 'imag-part '(polar) imag-part)
  (put 'magnitude '(polar) magnitude)
  (put 'angle '(polar) angle)
  (put 'make-from-real-imag 'polar
       (lambda (x y) 
         (tag (make-from-real-imag x y))))
  (put 'make-from-mag-ang 'polar
       (lambda (r a) 
         (tag (make-from-mag-ang r a))))
  (put 'equ? '(polar polar)
       (lambda (z1 z2) 
         (equ? z1 z2)))
  (put '=zero? '(polar)
       (lambda (x) (and (= (magnitude x) 0)
                        (= (angle x) 0))))
  'done)

(define (install-complex-package)
  ;; imported procedures from rectangular and polar packages
  (define (make-from-real-imag x y)
    ((get 'make-from-real-imag 
          'rectangular) 
     x y))
  (define (make-from-mag-ang r a)
    ((get 'make-from-mag-ang 'polar) 
     r a))
  (define (real-part z) (apply-generic 'real-part z))
  (define (imag-part z) (apply-generic 'imag-part z))
  (define (magnitude z) (apply-generic 'magnitude z))
  (define (angle z) (apply-generic 'angle z))
  ;; internal procedures
  (define (add-complex z1 z2)
    (make-from-real-imag 
     (+ (real-part z1) (real-part z2))
     (+ (imag-part z1) (imag-part z2))))
  (define (sub-complex z1 z2)
    (make-from-real-imag 
     (- (real-part z1) (real-part z2))
     (- (imag-part z1) (imag-part z2))))
  (define (mul-complex z1 z2)
    (make-from-mag-ang 
     (* (magnitude z1) (magnitude z2))
     (+ (angle z1) (angle z2))))
  (define (div-complex z1 z2)
    (make-from-mag-ang 
     (/ (magnitude z1) (magnitude z2))
     (- (angle z1) (angle z2))))
  ;; interface to rest of the system
  (define (tag z) (attach-tag 'complex z))
  (put 'add '(complex complex)
       (lambda (z1 z2) 
         (tag (add-complex z1 z2))))
  (put 'sub '(complex complex)
       (lambda (z1 z2) 
         (tag (sub-complex z1 z2))))
  (put 'mul '(complex complex)
       (lambda (z1 z2) 
         (tag (mul-complex z1 z2))))
  (put 'div '(complex complex)
       (lambda (z1 z2) 
         (tag (div-complex z1 z2))))
  (put 'make-from-real-imag 'complex
       (lambda (x y) 
         (tag (make-from-real-imag x y))))
  (put 'make-from-mag-ang 'complex
       (lambda (r a) 
         (tag (make-from-mag-ang r a))))
  (put 'real-part '(complex) real-part)
  (put 'imag-part '(complex) imag-part)
  (put 'magnitude '(complex) magnitude)
  (put 'angle '(complex) angle)
  (put 'equ? '(complex complex) equ?)
  (put '=zero? '(complex) =zero?)
  (put 'project '(complex)
       (lambda (x)(if (= (imag-part x) 0)
        (make-real(real-part x))
         #f)))
  'done)

(define (add x y) (apply-generic 'add x y))
(define (sub x y) (apply-generic 'sub x y))
(define (mul x y) (apply-generic 'mul x y))
(define (div x y) (apply-generic 'div x y))
(define (equ? x y) (apply-generic 'equ? x y))
(define (=zero? x) (apply-generic '=zero? x))
(define (exp x y) (apply-generic 'exp x y))
(define (raise x) (apply-generic 'raise x))
(define (project x) (apply-generic 'project x))

(define (make-integer n)
  ((get 'make 'integer) n))

(define (make-real n)
  ((get 'make 'real) n))

(define (make-complex-from-real-imag x y)
  ((get 'make-from-real-imag 'complex) x y))

(define (make-complex-from-mag-ang r a)
  ((get 'make-from-mag-ang 'complex) r a))

(install-rectangular-package)
(install-polar-package)
(install-integer-package)
(install-complex-package)
;(install-rational-package)
(install-real-package)

(define (install-coercion-package)
;;internal definintion
(define (integer->complex n)
  (make-complex-from-real-imag 
   (contents n) 0))
(define (integer->integer n) n)
(define (complex->complex z) z)
(define (rational->rational r) r)
(define (real->real r) r)   
;;External definition
(put-coercion 'integer 'integer integer->integer)
(put-coercion 'complex 'complex complex->complex)
(put-coercion 'integer 'complex integer->complex)
(put-coercion 'rational 'rational rational->rational)
(put-coercion 'real 'real real->real)
'done)

(install-coercion-package)

(sub (make-complex-from-real-imag 4 1)
     (make-complex-from-real-imag 3 1))

(define proc (get 'sub '(complex complex)))

(define args (list (make-complex-from-real-imag 4 1)
     (make-complex-from-real-imag 3 1)))

(display (apply proc (map contents args)))

如果我在 之后应用 drop 程序 ,代码工作正常,即 (drop (apply-generic op .args)) 工作正常。但是我实现的方式 apply-generic(drop (apply-generic op .args)) 应该等同于

(define (apply-generic op . args)
    (let ((type-tags (map type-tag args)))
      (let ((proc (get op type-tags)))
        (if proc
           (drop (apply proc (map contents args)))
            (if(element?(map
                 (lambda (x) (eq? x (car type-tags)))
                     type-tags) #f)
              (drop (iterator type-tags op type-tags args))
                (error "No method for these types"
                          (list op type-tags))
        )))))

但它给我的错误是 Bad tagged datum: TYPE-TAG 4 。请帮忙。

原来我没有设定条件只在执行算术运算时应用 drop,因此 apply-generic 过程试图 drop 当我使用 real-part z 并给我类型标签错误时的参数。

最终代码:

(define arithmetic (list 'add 'sub 'mul 'div))

(define (apply-generic op . args)
    (let ((type-tags (map type-tag args)))
      (let ((proc (get op type-tags)))
        (if proc
           (if (element? arithmetic op)          
              (drop (apply proc (map contents args)))
              (apply proc (map contents args)))
            (if (element? (map
                   (lambda (x) (eq? x (car type-tags)))
                       type-tags) #f)
                (iterator type-tags op type-tags args)
                (error "No method for these types"
                       (list op type-tags))
        )))))

我在执行 install-real-packageequ? 过程时也遇到了一些问题,但它们是无关的

我也最终更改了我的 drop 程序以符合练习的参数

(define (drop num)
  (if (and (not (eq? (type-tag num) 'integer))
           (equ? (raise (project num)) num))
     (drop (project num))
     num))