SICP問題2.87

汎用演算パッケージに多項式用の=zero?を設定せよ。

2.78で示した型機構を使った汎用演算システムが実装してあると仮定するということなので、SICP問題2.78で使用していた定義など。

(define (attach-tag type-tag contents)
  (if (eq? type-tag 'scheme-number)
      contents
      (cons type-tag contents)))
(define (type-tag datum)
  (if (number? datum)
      'scheme-number
      (if (pair? datum)
	  (car datum)
	  (error "Bad tagged datum -- TYPE-TAG" datum))))
(define (contents datum)
  (if (number? datum)
      datum
      (if (pair? datum)
	  (cdr datum)
	  (error "Bad tagged datum -- CONTENTS" datum))))
;; この時のapply-generic(教科書で定義)
(define (apply-generic op . args)
  (let ((type-tags (map type-tag args)))
    (let ((proc (get op type-tags)))
      (if proc
	  (apply proc (map contents args))
	  (error 
	   "No method for these types -- APPLY-GENERIC"
	   (list op type-tags))))))
; 汎用演算子(教科書で定義)
(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))

scheme-numberパッケージの定義

; scheme-number-package(教科書で定義)
(define (install-scheme-number-pacakge)
  (define (tag x)
    (attach-tag 'scheme-number x))
  (put 'add '(scheme-number scheme-number)
       (lambda (x y) (tag (+ x y))))
  (put 'sub '(scheme-number scheme-number)
       (lambda (x y) (tag (- x y))))
  (put 'mul '(scheme-number scheme-number)
       (lambda (x y) (tag (* x y))))
  (put 'div '(scheme-number scheme-number)
       (lambda (x y) (tag (/ x y)))) ; 2009/11/16 +になっていたので/に修正
  (put 'make 'scheme-number
       (lambda (x) (tag x)))
  (put '=zero? '(scheme-number) 
       (lambda (x) (= x 0)))
  'done)
(define (make-scheme-number n)
  ((get 'make 'scheme-number) n))
(install-scheme-number-pacakge)

多項式の汎用演算システムへの組み込み。
=zero?も定義。

(define (install-polynomial-package)
  ;; 内部手続き
  ;; 多項式の表現
  (define (make-poly variable term-list)
    (cons variable term-list))
  (define (variable p) (car p))
  (define (term-list p) (cdr p))
  (define (variable? x) (symbol? x))
  (define (same-variable? v1 v2)
    (and (variable? v1) (variable? v2) (eq? v1 v2)))
  ;; 項とリストの表現
  ; 多項式の輪のリストを構成する手続き
  (define (add-terms L1 L2)
    (cond ((empty-termlist? L1) L2)
          ((empty-termlist? L2) L1)
          (else
           (let ((t1 (first-term L1))
                 (t2 (first-term L2)))
             (cond ((> (order t1) (order t2))
                    (adjoin-term 
                     t1 (add-terms (rest-terms L1) L2)))
                   ((< (order t1) (order t2))
                    (adjoin-term
                     t2 (add-terms L1 (rest-terms L2))))
                   (else
                    (adjoin-term
                     (make-term (order t1)
                                (add (coeff t1) (coeff t2)))
                     (add-terms (rest-terms L1) (rest-terms L2)))))))))
  
  ; 二つの多項式の乗の項リスト
  (define (mul-terms L1 L2)
    (if (empty-termlist? L1)
        (the-empty-termlist)
        (add-terms (mul-term-by-all-terms (first-term L1) L2)
                   (mul-terms (rest-terms L1) L2))))
  (define (mul-term-by-all-terms t1 L)
    (if (empty-termlist? L)
        (the-empty-termlist)
        (let ((t2 (first-term L)))
          (adjoin-term
            (make-term (+ (order t1) (order t2))
                       (mul (coeff t1) (coeff t2)))
            (mul-term-by-all-terms t1 (rest-terms L))))))
  (define (adjoin-term term term-list)
    (if (=zero? (coeff term))
        termlist
        (cons term term-list)))
  (define (the-empty-termlist) '())
  (define (first-term term-list) (car term-list))
  (define (rest-terms term-list) (cdr term-list))
  (define (empty-termlist? term-list) (null? term-list))
  (define (make-term order coeff) (list order coeff))
  (define (order term) (car term))
  (define (coeff term) (cadr term))
  (define (add-poly p1 p2)
    (if (same-variable? (variable p1) (variable p2))
        (make-poly (variable p1)
                   (add-terms (term-list p1)
                              (term-list p2)))
        (error "Polys not in same var -- ADD-POLY"
               (list p1 p2))))
  (define (mul-poly p1 p2)
    (if (same-variable? (variable p1) (variable p2))
        (make-poly (variable p1)
                   (mul-terms (term-list p1)
                              (term-list p2)))
        (error "Polys not in same var -- MUL-POLY"
               (list p1 p2))))
  (define (=zero-poly? L)
    (define (=zero-term? x)
      (or (empty-termlist? x)
          (if (=zero? (coeff (first-term x))) ; 2009/11/09 = 0 となっていのを修正
              (=zero-term? (rest-terms x))
              #f)))
    (=zero-term? (term-list L)))
  ;; システムの他の部分とのインタフェース
  (define (tag p) (attach-tag 'polynomial p))
  (put 'add '(polynomial polynomial)
       (lambda (p1 p2) (tag (add-poly p1 p2))))
  (put 'mul '(polynomial polynomial)
       (lambda (p1 p2) (tag (mul-poly p1 p2))))
  (put 'make 'polynomial
       (lambda (var terms) (tag (make-poly var terms))))
  (put '=zero? '(polynomial)
       (lambda (x) (=zero-poly? x)))
  'done)
(install-polynomial-package)
(define (make-polynomial var terms)
  ((get 'make 'polynomial) var terms))

テスト 2009/11/09テスト結果載せてなかったので修正

(add (make-polynomial 'X '((5 3) (4 2) (3 2)))
     (make-polynomial 'X '((6 4) (4 3) (2 2))))
; (polynomial X (6 4) (5 3) (4 5) (3 2) (2 2))

(mul (make-polynomial 'X '((5 3) (4 2)))
     (make-polynomial 'X '((6 4) (4 3))))
; (polynomial X (11 12) (10 8) (9 9) (8 6))

(=zero? (make-polynomial 'X '((5 3) (4 2))))
; #f

(=zero? (make-polynomial 'X '()))
; #t
(=zero? (make-polynomial 'X '((5 0) (4 0))))
; #t

(=zero? (make-polynomial 'X '((5 3) (4 0))))
; #f

OK