SICP 問題 2.3(長方形の周囲の長さと面積を求める)
【問題】
平面上の長方形 [rectangle] の表現を実装せよ。(ヒント:問題 2.2 が使いたくなるであろう。)
構成子と選択子を使い、与えられた長方形の周囲の長さ [perimeter] と面積 [area] を計算する手続きを作れ。
次に長方形の違う表現を実装せよ。同じ周囲の長さと面積の手続きが、どちらの表現でも働くように、システムに良好な抽象の壁で設計されているか。
【解答】
まずは解答を先に。
その1 線分をそのまま長方形の表現とする
;長方形を生成する手続き (define (make-rectangle s) s) ;写像を求める抽象化した手続き (define (rectangle-map-length r i-point) (abs (- (i-point (start-segment r)) (i-point (end-segment r))))) ;長方形のx写像の長さを求める手続き (define (rectangle-x-map-length r) (rectangle-map-length r x-point)) ;長方形のy写像の長さを求める手続き (define (rectangle-y-map-length r) (rectangle-map-length r y-point))
その2 ((基点x . 基点y) x写像長 . y写像長) で長方形を表現する
;長方形を生成する手続き (define (make-rectangle s) ;座標をすべて取得 (let* ((p0 (start-segment s)) (p1 (end-segment s)) (x0 (x-point p0)) (x1 (x-point p1)) (y0 (y-point p0)) (y1 (y-point p1))) ;((基点x . 基点y) . (x長 . y長)) で長方形を表現してみる。 (cons (make-point (if (< x0 x1) x0 x1) (if (< y0 y1) y0 y1)) (cons (abs (- x0 x1)) (abs (- y0 y1)))))) ;長方形のx写像の長さを求める手続き (define (rectangle-x-map-length r) (car (cdr r))) ;長方形のx写像の長さを求める手続き (define (rectangle-y-map-length r) (cdr (cdr r)))
上記の2種の表現に共通して使用する、周囲の長さと面積を求める手続き
;長方形を受け取って周囲の長さを取得する手続き (define (perimeter r) (* 2 (+ (rectangle-x-map-length r) (rectangle-y-map-length r)))) ;長方形を受け取って面積を取得する手続き (define (area r) (* (rectangle-x-map-length r) (rectangle-y-map-length r)))
実行結果
どっちの表現で計算しても同じ値になると。
gosh> (define r0 (make-rectangle (make-segment (make-point 5 10) (make-point -2 1))))
r0
gosh> (define r1 (make-rectangle (make-segment (make-point 5 10) (make-point -2 12))))
r1
gosh> r0
((5 . 10) -2 . 1)
gosh> r1
((5 . 10) -2 . 12)
gosh>
gosh> (perimeter r0)
32
gosh> (area r0)
63
gosh> (perimeter r1)
18
gosh> (area r1)
14
gosh> (perimeter r0)
32
gosh> (area r0)
63
gosh> (perimeter r1)
18
gosh> (area r1)
14
思考過程
さて、思考過程をトレースするほど大した問題じゃないんだが一応まとめよう。
「その1」のパターンは、要するに線分が与えられれば長方形は決まるよねぇという発想。
ヒントはそういう意味にしかとれないんだが。。別の解釈があるんだろうか?
このパターンでは、x、y それぞれの写像を求める手続きを抽象化可能だったことぐらいか?
最初こんなでしたから。
;長方形のx写像の長さを求める手続き・・・① (define (rectangle-x-map-length r) (abs (- (x-point (start-segment r)) (x-point (end-segment r))))) ;長方形のy写像の長さを求める手続き・・・② (define (rectangle-y-map-length r) (abs (- (y-point (start-segment r)) (y-point (end-segment r)))))
明らかに、外から x-point か y-pointを渡してやれば済む感じなので纏めた。
その2はこれも「別の表現」にするならもうこれぐらいしか思いつかなかったんだけど、
終わってみれば make-rectangle に渡す引数を座標にしてやるとか、いろいろあったか。
いや、それじゃ make-segment と変わらんか。結局同じだな。
要するに、perimeter や area 内で呼ばれている、rectangle-x-map-length と、
rectangle-y-map-length のインタフェースを同じにしてやればいいってことだよね、コレって。