SICP 問題 2.81(同型の強制型変換は必要か?)
問題
Louis Reasonerは、apply-genericは引数が既に同じ型を持っていても、互いの型へ強制変換を試みるべきだと考えた。そこで彼が考えるには、上に示した強制変換の表に、各型の引数を自分の型へ、「強制変換」させる手続きを置く必要がある。例えば、上に示したscheme-number->complex型変換に加え、彼は
(define (scheme-number->scheme-number n) n) (define (complex->complex z) z) (put-coercion 'scheme-number 'scheme-number scheme-number->scheme-number) (put-coercion 'complex 'complex complex->complex)
としようとする。
a.
Louisの強制型変換手続きが設定されると、apply-genericが型scheme-numberの二つの引数や型complexの二つの引数で、これらの型で表に見つからない手続きに対して呼び出されると、何が起きるか。例えば汎用べき乗演算
(define (exp x y) (apply-generic 'exp x y))
が定義してあり、scheme数パッケージのべき乗の手続き
;次はScheme数パッケージへ追加する。 (put 'exp '(scheme-number scheme-number) (lambda (x y) (tag (expt x y)))) ;基本手続きexptを使う
はあるが、他のパッケージにはないとしよう。引数に二つの複素数を持ってexpを呼び出すと何が起きるか
b.
同じ型の引数の強制型変換について何かすべきだというLouisは正しいか。それともこのままapply-genericは正しく働くか。
c.
二つの引数が同じ型を持っていれば、強制型変換を試みないように、apply-genericを修正せよ。
解答
put-coercionとかget-coercionの実装がアレなので脳内だけでやっちゃいたいなぁ。。動きとかわかんねぇし。
a.
メソッドがないよと怒られるのかと思ったら、なんか無限ループする感じ?
(やっぱりput-coercion/get-coercionを実装して試してみるべきか〜?)
b.
無限ループするなら、定義しちゃいけないんじゃない?
c.
こんな感じ?
(define (apply-generic op . args) (let* ((type-tags (map type-tag args)) (proc (get op type-tags))) (if proc (apply proc (map contents args)) (if (= (length args) 2) (let* ((type1 (car type-tags)) (type2 (cadr type-tags))) ;同じ型の場合は強制型変換をしないようにする。 (if (eq? type1 type2) (error "No method for these types" (list op type-tags)) ;異なる型だった場合は変換手続きを取得する (let* ((a1 (car args)) (a2 (cadr args)) (t1->t2 (get-coercion type1 type2)) (t2->t1 (get-coercion type2 type1))) (cond (t1->t2 (apply-generic op (t1->t2 a1) a2)) (t2->t1 (apply-generic op a1 (t2->t1 a2))) (else (error "No method for these types" (list op type-tags))))) (error "No method for these types" (list op type-tags))))))