SICP 読み (69) 2.5.2 異る型のデータの統合
問題 2.85 の drop を検討してみる。
raise の逆で型の塔を下げる手続き。問題によれば project で下げたものを raise してみて元と同じかどうかを判断すれば良いとの事。という事は、まず project の検討をしてみれば良いのか。
それぞれの型について考えてみると
- complex の project は実数部を make-real に渡す手続きで良い
- real の project は小数点以下を切りステた値を make-rational の分子に渡す手続きで良い。分母には 1 を渡せば問題ナシ。
- rational の project は (/ 分子 分母) の小数点以下を切りステた値を make-integer に渡す手続きで良い
上記を踏まえて、まず試験を書いてみる。乱暴気味ですがこんなカンジか。
("project test" (setup (lambda () (install-integer-package) (install-rational-package) (install-real-package) (install-complex-package) (install-rectangular-package) (install-polar-package))) ("complex -> real" (let ((t1 (make-complex-from-real-imag 1 0)) (t2 (make-complex-from-real-imag 1 1))) (assert-equal 'real (type-tag (project t1))) (assert-true (equ? (make-real 1) (project t1))) (assert-true (equ? (make-real 1) (project t2)))) ) ("real -> rational" (let ((t1 (make-real 1.0)) (t2 (make-real 1.5))) (assert-equal 'rational (type-tag (project t1))) (assert-true (equ? (make-rational 1 1) (project t1))) (assert-true (equ? (make-rational 1 1) (project t2)))) ) ("rational -> integer" (let ((t1 (make-rational 1 1)) (t2 (make-rational 1 2))) (assert-equal 'integer (type-tag (project t1))) (assert-true (equ? (make-integer 1) (project t1))) (assert-true (equ? (make-integer 0) (project t2)))) ) ("integer -> ?" (assert-error (lambda () (project (make-integer 1)))) ) )
って、とりあえず気なる部分はあれど、各 package に project を実装してみる。まず、complex なソレが以下 (一部分のみ)
(put 'project '(complex) (lambda (x) (make-real (real-part x))))
次は real
(put 'project '(real) (lambda (x) (make-rational x 1)))
最後に rational
(put 'project '(rational) (lambda (x) (make-integer (floor (/ (number x) (denom x))))))
これ、ちょっと微妙。
で、試験してみたんですが、
*** ERROR: unbound variable: project
って言われて、_何でよ_と思っていたらトップレベルでの定義を忘れてた。(を
(define (project x) (apply-generic 'project x))
とほほ。
修正後も試験失敗。
./lib/2.5.2.scm:137: (gcd n d) Error occurred in real -> rational *** ERROR: integer required, but got 1.5 ./lib/2.5.2.scm:137: (gcd n d) E ./test/test-2.5.2.scm:36: (assert-equal 'integer (type-tag (project t1))) Error occurred in rational -> integer *** ERROR: unbound variable: number ./test/test-2.5.2.scm:36: (assert-equal 'integer (type-tag (project t1)))
まず上側のですが、gcd には整数を渡さないとダメらしい。
gosh> (gcd 1.5 1) *** ERROR: integer required, but got 1.5 Stack Trace: _______________________________________ 0 (error "integer required, but got" arg) At line 57 of "/usr/share/gauche/0.8.7/lib/gauche/numerical.scm" 1 (recn (rec arg (car args)) (cdr args)) At line 54 of "/usr/share/gauche/0.8.7/lib/gauche/numerical.scm" 2 (map (lambda (arg) (unless (integer? arg) (error "integer required ... At line 55 of "/usr/share/gauche/0.8.7/lib/gauche/numerical.scm" gosh>
って、小数点切りステって自分で書いてあるのにそうしてないし。実装を以下な形で修正。(real の project)
(put 'project '(real) (lambda (x) (make-rational (floor x) 1)))
しかし切りステって floor で良いのかなぁ。
で、もう一つは typo でした (rational な project)
(put 'project '(rational) (lambda (x) (make-integer (floor (/ (numer x) (denom x))))))
number ではなくって numer
とりあえず試験は通ったんですが、大丈夫なのかなぁ。次は drop の検討。