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 の検討。