SICPを読む(88) 問題 2.77-2.80 汎用演算システム

汎用演算システムに入ります。パッケージの定義が単調過ぎてあんまり面白みが無い。

問題 2.77

'(complex rectanglar 3 . 4)

に対して、

  • (magnitude z)を評価したら、ダメだった。complexパッケージに、アクセサの手続きを加えたら、うまく動いた。何故か?
  • apply-genericは何回呼ばれるか?

という問題。

complexパッケージは、手続きを直交座標、極座標パッケージに操作を委譲してる形になってるのがポイント。

  • apply-genericは、とりあえず、car部にあるcomplexタグと、magnitudeを探しにいくので、エラーとなる。
  • 手続きを追うと
    • (magnitude '(complex rectanglar 3 . 4))
    • (apply-generic 'magnitude '(complex rectanglar 3 . 4))
    • (magnitude '(rectanglar 3 . 4))
    • (apply-generic 'magnitude '(rectanglar 3 . 4))
    • magnitudeパッケージの、rectanglarが呼ばれる。
  • complex,rectanglarを見に行くので、2回。

難しくはないとおもう。

traceしてみると、

(magnitude (make-complex-from-real-imag 3 4))
|(apply-generic magnitude (complex rectanglar 3 . 4))
|(apply-generic magnitude (rectanglar 3 . 4))
|5
5

正解!

問題 2.78

ちょっと迷ったけど、クリア!

(define (attach-tag type-tag contents)
  (if (eq? type-tag 'scheme-number)
      contents
      (cons type-tag contents)))
(define (type-tag datum)
  (cond ((number? datum) 'scheme-number)
        ((pair? datum) (car datum))
        (else (error "Bad tagged datum -- TYPE-TAG" datum))))
(define (contents datum)
  (cond ((number? datum) datum)
        ((pair? datum) (cdr datum))
        (else (error "Bad tagged datum -- CONTENTS" datum))))

テスト。

(add (make-scheme-numbr 2) (make-scheme-numbr 3))
(- (make-scheme-numbr 2) (make-scheme-numbr 1))
(mul 3 4)

どっちでも動く。

ちょっとしたこと

整数と小数の比較。

(eq? 0 0.0) ; #f
(eqv? 0 0.0) ; #f
(equal? 0 0.0) ; #f

全部#fになった。型を意識したほうが良いらしい。

Cだと、

printf("%s\n", 0 == 0.0 ? "いっしょ" : "ちがう");
/* いっしょ */

いっしょになる。

JSだと、

js> 0 == 0.0 ? "いっしょ" : "ちがう"
いっしょ

Rubyも。

irb(main):002:0> 0 == 0.0
=> true

どうやら、Schemeは特殊っぽい。

問題 2.79,2.80

面倒なので、回答はcomplexだけ。

  (put 'equ? '(complex complex)
       (lambda (x y)
         (and (eq? (real-part x) (real-part y))
              (eq? (imag-part x) (imag-part y)))))
  (put '=zero? '(complex)
       (lambda (x)
         (and (zero? (real-part x)) (zero? (imag-part x)))))

まとめ

解説が長い。ちょっと飽き気味。