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 のインタフェースを同じにしてやればいいってことだよね、コレって。