SICP 読み (88) 2.5.3 例: 記号代数
問題 2.92 はスルー。あと 5 問で 2 章が終わるんですが、残りの 3 頁が見るからに重たそう。ってか一番重いの 2.92 なんだろうな。
問題 2.93
まず、問題 2.88 を流用して有理数なソレを盛り込む。現時点で include はしてるんですが、仕様通りの実装にはなっていないハズ。
(define (install-rational-package) ;; internal procedures (define (numer x) (car x)) (define (denom x) (cdr x)) (define (make-rat n d) (let ((g (gcd n d))) (cons (/ n g) (/ d g)))) (define (add-rat x y) (make-rat (+ (* (numer x) (denom y)) (* (numer y) (denom x))) (* (denom x) (denom y)))) (define (sub-rat x y) (make-rat (- (* (numer x) (denom y)) (* (numer y) (denom x))) (* (denom x) (denom y)))) (define (mul-rat x y) (make-rat (* (numer x) (numer y)) (* (denom x) (denom y)))) (define (div-rat x y) (make-rat (* (numer x) (denom y)) (* (denom x) (numer y)))) ;; interface to rest of the system (define (tag x) (attach-tag 'rational x)) (put 'add '(rational rational) (lambda (x y) (tag (add-rat x y)))) (put 'sub '(rational rational) (lambda (x y) (tag (sub-rat x y)))) (put 'mul '(rational rational) (lambda (x y) (tag (mul-rat x y)))) (put 'div '(rational rational) (lambda (x y) (tag (div-rat x y)))) (put 'make 'rational (lambda (n d) (tag (make-rat n d)))) 'done) (define (make-rational n d) ((get 'make 'rational) n d))
make-rat を変更し、とあるな。単純に cons するだけにしておけば動く??
上記実装 (make-rat は変更) を以下の試験で確認。
("include rational package" (setup (lambda () (install-rational-package) (install-scheme-number-package) (install-polynomial-package))) ("sample" (let ((p1 (make-polynomial 'x '((2 1) (0 1)))) (p2 (make-polynomial 'x '((3 1) (0 1))))) (let ((rf (make-rational p2 p1))) (assert-equal 'rational (car rf)) (assert-equal p2 (cadr rf)) (assert-equal p1 (cddr rf)) )) ) )
一応動いている。次は (add rf rf) で約分されていない事を確認か。assert を追加。
("include rational package" (setup (lambda () (install-rational-package) (install-scheme-number-package) (install-polynomial-package))) ("sample" (let ((p1 (make-polynomial 'x '((2 1) (0 1)))) (p2 (make-polynomial 'x '((3 1) (0 1))))) (let ((rf (make-rational p2 p1))) (assert-equal 'rational (car rf)) (assert-equal p2 (cadr rf)) (assert-equal p1 (cddr rf)) (let ((test (add rf rf))) (assert-equal 'rational (car test)) (assert-equal p1 (cddr test)) (assert-equal (add p2 p2) (cadr test)) ) )) ) )
試験してみると NG との事。
-- (test case) include rational package: E ./test/test-2.5.3.scm:20: (add rf rf) Error occurred in sample *** ERROR: operation * is not defined between (polynomial x (3 1) (0 1)) and (polynomial x (2 1) (0 1)) ./test/test-2.5.3.scm:20: (add rf rf)
演算子が + とか * とかになってるからか。- もあるが neg して add すれば OK ですな。上記の試験がビンゴなのかどうか、は実装を修正しないと分からん。(を
で、試験してみたんですが、上記の試験では NG。_分数を最低項まで引き下げ_んというのはこれですか。本来であれば
ではなくて
にならないとイケないんだな。でも今の実装ではそれは無理。(add rf rf) です。
修正した試験が以下。
("include rational package" (setup (lambda () (install-rational-package) (install-scheme-number-package) (install-polynomial-package))) ("sample" (let ((p1 (make-polynomial 'x '((2 1) (0 1)))) (p2 (make-polynomial 'x '((3 1) (0 1))))) (let ((rf (make-rational p2 p1))) (assert-equal 'rational (car rf)) (assert-equal p2 (cadr rf)) (assert-equal p1 (cddr rf)) (let ((test (add rf rf))) (assert-equal 'rational (car test)) (assert-equal (make-polynomial 'x '((4 1) (2 2) (0 1))) (cddr test)) (assert-equal (make-polynomial 'x '((5 2) (3 2) (2 2) (0 2))) (cadr test)) ) )) ) )
あと、修正した rational-package が以下。
(define (install-rational-package) ;; internal procedures (define (numer x) (car x)) (define (denom x) (cdr x)) (define (make-rat n d) ; (let ((g (gcd n d))) ; (cons (/ n g) (/ d g)))) (cons n d)) (define (add-rat x y) (make-rat (add (mul (numer x) (denom y)) (mul (numer y) (denom x))) (mul (denom x) (denom y)))) (define (sub-rat x y) (make-rat (add (mul (numer x) (denom y)) (neg (mul (numer y) (denom x)))) (mul (denom x) (denom y)))) (define (mul-rat x y) (make-rat (mul (numer x) (numer y)) (mul (denom x) (denom y)))) (define (div-rat x y) (make-rat (mul (numer x) (denom y)) (mul (denom x) (numer y)))) ;; interface to rest of the system (define (tag x) (attach-tag 'rational x)) (put 'add '(rational rational) (lambda (x y) (tag (add-rat x y)))) (put 'sub '(rational rational) (lambda (x y) (tag (sub-rat x y)))) (put 'mul '(rational rational) (lambda (x y) (tag (mul-rat x y)))) (put 'div '(rational rational) (lambda (x y) (tag (div-rat x y)))) (put 'make 'rational (lambda (n d) (tag (make-rat n d)))) 'done) (define (make-rational n d) ((get 'make 'rational) n d))
一応、手を入れた手続きについては試験をしておいた方が良いな。
追記
試験を作成。微妙だったのは scheme-number なパケジに neg 演算が無かったコト。最初、rational なパケジに neg を定義してしまい、微妙にハマる。
手を入れた scheme-number-package を以下に。
(define (install-scheme-number-package) (put 'add '(scheme-number scheme-number) +) (put 'sub '(scheme-number scheme-number) -) (put 'mul '(scheme-number scheme-number) *) (put 'div '(scheme-number scheme-number) /) (put 'neg '(scheme-number) -) (put '=zero? '(scheme-number) zero?) (put 'make 'scheme-number (lambda (x) x)) 'done) (define (make-scheme-number n) ((get 'make 'scheme-number) n))
で、演算子の試験が以下。簡単に済ませスギかも。
("rational package (add)" (let ((r1 (make-rational 1 2)) (r2 (make-rational 1 3))) (let ((result (add r1 r2))) (assert-equal 'rational (car result)) (assert-equal 5 (cadr result)) (assert-equal 6 (cddr result)))) ) ("rational package (sub)" (let ((r1 (make-rational 1 2)) (r2 (make-rational 1 3))) (let ((result (sub r1 r2))) (assert-equal 'rational (car result)) (assert-equal 1 (cadr result)) (assert-equal 6 (cddr result)))) ) ("rational package (mul)" (let ((r1 (make-rational 1 2)) (r2 (make-rational 1 3))) (let ((result (mul r1 r2))) (assert-equal 'rational (car result)) (assert-equal 1 (cadr result)) (assert-equal 6 (cddr result)))) ) ("rational package (div)" (let ((r1 (make-rational 1 2)) (r2 (make-rational 1 3))) (let ((result (div r1 r2))) (assert-equal 'rational (car result)) (assert-equal 3 (cadr result)) (assert-equal 2 (cddr result)))) )
一応パスしてると見て、remainder-terms の検討に着手か。2.91 の流用という部分で言えば、2.94 はさほどハードルは高くないな。がしかし、それ以降が微妙。