SICP 読み (62) 2.5.1 汎用算術演算
週末はばたばたしてて、何もできず。次の 2.5.2 節はハードル高そうなんですがマルチでできるのかどうなのか。
問題 2.80
2.79 よりはハードルが低そうなんですが、やる前に同じ風なコト言ってるしなぁ。
とりあえず scheme-number から。試験はこれで OK か。
("=zero? test" (let ((t1 (make-scheme-number 0)) (t2 (make-scheme-number 1))) (assert-true (=zero? t1)) (assert-false (=zero? t2))) )
あ、これは実装簡単だな。(今気づいた
(define (install-scheme-number-package) -- 中略 -- (put '=zero? 'scheme-number zero?) 'done)
で、試験してみたら失敗。トップレベルで手続きを作ってない。
(define (=zero? x) (apply-generic '=zero? x))
再度試験も失敗。メセジ確認すると
./test/test-2.5.2.scm:56: (assert-true (=zero? t1)) Error occurred in =zero? test *** ERROR: No method for these types -- APPLY-GENERIC (=zero? (scheme-number)) ./test/test-2.5.2.scm:56: (assert-true (=zero? t1))
(一部のみ抜粋)
との事。
apply-generic で get '=zero? (scheme-number)
になるんだよ。日を空けると忘れるなぁ。とりあえず、以下のように修正したら試験は通りました。
(put '=zero? '(scheme-number) zero?)
次は有理数ですが、試験は以下か。
("=zero? test" (let ((t1 (make-rational 0 0)) (t2 (make-rational 1 1))) (assert-true (=zero? t1)) (assert-false (=zero? t2))) )
すげぇいい加減感満点ですがとりあえず仕様確認はこれでできる。実装は
(define (install-rational-package) ;; internal procedures -- 中略 -- (define (=zero-rat? x) (and (zero? (numer x)) (zero? (denom x)))) ;; interface to rest of the system -- 中略 -- (put '=zero? '(rational) =zero-rat?) 'done)
みたいな感じで試験。通らん。やっぱ easy に考えスギですな。アセってやるとロクな事ないぞ、っていう好例かと。
見てみると最初の assert が #f になっている。確認してみると
gosh> (make-rational 0 0) (rational #<nan> . #<nan>) gosh>
なんじゃこれは、という事で make-rational 見てみると以下。
(define (make-rat n d) (let ((g (gcd n d))) (cons (/ n g) (/ d g))))
わははは。0 除算が通るのか。それは良いとしてどう取り扱えば良いかなぁ。
- n か d が 0 なら両方ゼロ
- g が 0 なら両方ゼロ
有理数として分子と分母な管理をしている、という事を踏まえて二番目案を採用。
(define (make-rat n d) (let ((g (gcd n d))) (if (zero? g) (cons 0 0) (cons (/ n g) (/ d g)))))
微妙なオトシ穴が結構あります。てか、ヤッツケ感満点だし。
complex は少しきちんと実装を検討した方が良いな。とりあえず試験は以下で。
("=zero? test (rectangular)" (let ((t1 (make-complex-from-real-imag 0 0)) (t2 (make-complex-from-real-imag 0 1)) (t3 (make-complex-from-real-imag 1 0)) (t4 (make-complex-from-real-imag 1 1))) (assert-true (=zero? t1)) (assert-false (=zero? t2)) (assert-false (=zero? t3)) (assert-false (=zero? t4))) ) ("=zero? test (polar)" (let ((t1 (make-complex-from-mag-ang 0 0)) (t2 (make-complex-from-mag-ang 0 1)) (t3 (make-complex-from-mag-ang 1 0)) (t4 (make-complex-from-mag-ang 1 1))) (assert-true (=zero? t1)) (assert-false (=zero? t2)) (assert-false (=zero? t3)) (assert-false (=zero? t4))) )
ぱっと見、ややこしい事はしてない風に見えるので easy 実装で良いのかなぁ。以下。
(define (install-rectangular-package) ;; internal procedures -- 中略 -- (define (=zero-rect? x) (and (zero? (real-part x)) (zero? (imag-part x)))) -- 中略 -- (put '=zero? '(rectangular) =zero-rect?) 'done) (define (install-polar-package) ;; internal procedures -- 中略 -- (define (=zero-polar? x) (and (zero? (magnitude x)) (zero? (angle x)))) -- 中略 -- (put '=zero? '(polar) =zero-polar?) 'done) -- 中略 -- (define (install-complex-package) ;; imported procedures from rectangular and polar packages -- 中略 -- (put '=zero? '(complex) =zero?) 'done)
なんか自分で書いてて略しすぎでワケワカ。しかも equ? と =zero? が (以下略
一応上記で試験はパス。
2.5.2 節以降、密度が濃い。本職も多忙な上、マルチなソレもあったりハードルが高かったりして、ノルマを設定した方が良いかな、と思っている今日この頃。