SICP問題2.89

濃い多項式に適しているという項リストの表現を実装する手続きを定義する。
濃い多項式に適している表現では
x^5 + 2x^4 + 3x^2 - 2x - 5

(1 2 0 3 -2 -5)

と表される。これだとリストの項目数-1=最大の次数となる。
polynomial-packageの修正。
選択子をちょろっと修正する位と思ったら次数によってリストの長さが変わるのでmulが面倒臭かった。

(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 L1) (order L2)) ; order の修正に伴い修正
                    (adjoin-term 
                     t1 (add-terms (rest-terms L1) L2)))
                   ((< (order L1) (order L2)) ; order の修正に伴い修正
                    (adjoin-term
                     t2 (add-terms L1 (rest-terms L2))))
                   (else
                    (adjoin-term
                     (make-term (order L1) ; order の修正に伴い修正
                                (add (coeff t1) (coeff t2)))
                     (add-terms (rest-terms L1) (rest-terms L2))))))))) ; 修正
  
  ; 二つの多項式の乗の項リスト
  ; 追加 リストを拡張する手続き
  (define (extend-term L n)
    (define (extend-term-sub Ls ns)
      (if (<= ns 0)
          Ls
          (extend-term-sub (adjoin-term 0 Ls) (- ns 1))))
    (reverse (extend-term-sub (reverse L) (- n (order L)))))
  (define (mul-terms L1 L2)
    (if (empty-termlist? L1)
        (the-empty-termlist)
        (add-terms (mul-term-by-all-terms (order L1) (first-term L1) L2) ; 修正
                   (mul-terms (rest-terms L1) L2))))
  ; 修正 t1の次数+Lの最大次数にリストを拡張してmul手続きを適用する
  (define (mul-term-by-all-terms n t1 L)
    (map (lambda (x) (* t1 x)) (extend-term L (+ n (order L)))))
  (define (adjoin-term term termlist) ; 修正
    (cons term termlist))
  (define (the-empty-termlist) '())
  (define (first-term termlist) (car termlist)) ; 修正
  (define (rest-terms termlist) (cdr termlist))
  (define (empty-termlist? termlist) (null? termlist))
  (define (make-term order coeff) coeff) ; 修正 (本当はいらない)
  (define (order termlist) (if (pair? termlist)
                               (- (length termlist) 1)
                                0)) ; 修正
  (define (coeff term) 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? (first-term x))
              (=zero-term? (rest-terms x))
              #f)))
    (=zero-term? (term-list L)))
  (define (negate-termlist L)
    (if (empty-termlist? L)
        L
        (let ((f (first-term L))
              (r (rest-terms L)))
          (adjoin-term
           (make-term (order L) ; order の修正に伴い修正
                      (negate (coeff f)))
           (negate-termlist r)))))
  (define (sub-poly x y)
    (add-poly x 
              (make-poly (variable y)
                         (negate-termlist (term-list y)))))
  ;; システムの他の部分とのインタフェース
  (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)))
  ;; 追加
  (put 'negate '(polynomial)
       (lambda (x) (tag (make-poly (variable x)
                                   (negate-termlist (term-list x))))))
  (put 'sub '(polynomial polynomial)
       (lambda (p1 p2) (tag (sub-poly p1 p2))))
  'done)
(install-polynomial-package)
(define (make-polynomial var terms)
  ((get 'make 'polynomial) var terms))

テスト

(=zero? (make-polynomial 'X '(5 3 2 0 1)))
; #f
(=zero? (make-polynomial 'X '(0 0 0)))
#t
(=zero? (make-polynomial 'X '()))
#t
(add (make-polynomial 'X '(5 3 2 0 0))
     (make-polynomial 'X '(6 3 2 0 1)))
; (polynomial X 11 6 4 0 1)
(sub (make-polynomial 'X '(5 3 2 0 0))
     (make-polynomial 'X '(6 3 2 0 1)))
; (polynomial X -1 0 0 0 -1)
(mul (make-polynomial 'x '(5 1))
     (make-polynomial 'x '(5 -1)))
; (polynomial x 25 0 -1)
(mul (make-polynomial 'X '(5 3 2 0 0))
     (make-polynomial 'X '(6 3 2 0 1)))
; (polynomial X 30 33 31 12 9 3 2 0 0)