Posted at

SICP読書録 #18 2.1.2 抽象化の壁-練習問題 2.3

hiroshi-manabe様の日本語訳を使わせてもらいます。

練習問題等はGaucheで実行。

前回はこちら


2.1.2 抽象化の壁

前回、有理数を扱うための手続き群を作成したことで、有理数を扱うプログラムと、有理数の実装とを抽象化の壁で隔てることができた。

例えばnumerdenomが呼ばれてから有理数を簡約にしたほうがいいじゃないかと思い立って、以下のように書き換えたとしても・・・


_.scm

(define (make-rat n d) (cons n d))

(define (numer x)
(let ((g (gcd (car x) (cdr x))))
(/ (car x) g)))
(define (denom x)
(let ((g (gcd (car x) (cdr x))))
(/ (cdr x) g)))

これを使ってたプログラムは変更しなくてすむのだ。


練習問題 2.2

平面上の線分を表現できるようにしてみよう。線分は始点と終点という点のペアで表現する。

まずは点を表現できなくてはいけないだろう。コンストラクタとXY座標のセレクタ、そして表示を行う手続きを作る。


_.scm

(define (make-point x y)

(cons x y))
(define (x-point p)
(car p))
(define (y-point p)
(cdr p))
(define (print-point p)
(print "(" (x-point p) "," (y-point p) ")"))

これで、線分のコンストラクタ、始点と終点のセレクタを作れる。


_.scm

(define (make-segment s e)

(cons s e))
(define (start-segment sg)
(car sg))
(define (end-segmeint sg)
(cdr sg))

では、線分の中点を返す手続きを作ろう。


_.scm

(define (midpoint-segment sg)

(define (average a b) (/ (+ a b) 2))
(let ((s (start-segment sg))
(e (end-segment sg)))
(make-point (average (x-point s) (x-point e))
(average (y-point s) (y-point e)))))

実行結果。


_.scm

gosh> (define sg (make-segment (make-point 10 20) (make-point 110 120)))

gosh> (print-point (midpoint-segment sg))
(60,70)


練習問題 2.3

線分が表現できたら、次は長方形を表現してみよう。

先ほど作成した、点を表現するための手続き群を用いて、始点と終点のペアを保持することで表現する。


_.scm

(define (make-rectangle p1 p2)

(cons p1 p2))
(define (width-rectangle r)
(abs (- (x-point (car r)) (x-point (cdr r)))))
(define (height-rectangle r)
(abs (- (y-point (car r)) (y-point (cdr r)))))

周囲長と面積を計算する手続きを用意する。


_.scm

(define (perimeter-rectangle r)

(* 2 (+ (width-rectangle r) (height-rectangle r))))
(define (area-rectangle r)
(* (width-rectangle r) (height-rectangle r)))

実行結果。


_.scm

gosh> (define r (make-rectangle (make-point 10 10) (make-point 110 210)))

gosh> (width-rectangle r)
100
gosh> (height-rectangle r)
200
gosh> (perimeter-rectangle r)
600
gosh> (area-rectangle r)
20000

さて、内部表現を始点と終点ではなく、幅と高さのペアとするように修正してみよう。


_.scm

(define (make-rectangle p1 p2)

(cons (abs (- (x-point p1) (x-point p2)))
(abs (- (y-point p1) (y-point p2)))))
(define (width-rectangle r)
(car r))
(define (height-rectangle r)
(cdr r))

こちらのバージョンでも、先ほどの周囲長と面積を求める手続きは修正の必要なく、そのまま使える。