Решение упражнения 2.3 из SICP

17 January, 2008 (21:29) | Решения упражнений

Я рассмотрю это упражнение в несколько упрощенной форме. Будем считать, что прямоугольники ориентированы вдоль осей координат. Более общий случай рассмотрен, например, на SICP Wiki, но и упрощенного варианта вполне достаточно для демонстрации основных идей.

При любом представлении прямоугольника его периметр равен удвоенной сумме длин смежных сторон, а площадь равна произведению длин смежных сторон:

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

Первый способ представления прямоугольников – это представление двумя противоположными угловыми точками (я их назвал левой верхней и правой нижней, но это не принципиально). Это представление задается так:

(define (make-rectangle topleft bottomright) 
  (cons topleft bottomright))
(define (rectangle-topleft rectangle) 
  (car rectangle))
(define (rectangle-bottomright rectangle) 
  (cdr rectangle))
(define (rectangle-width rectangle) 
  (abs (- (x-point (rectangle-topleft rectangle)) 
          (x-point (rectangle-bottomright rectangle)))))
(define (rectangle-height rectangle) 
  (abs (- (y-point (rectangle-topleft rectangle)) 
          (y-point (rectangle-bottomright rectangle)))))

Второй способ представления прямоугольников – заданием левого верхнего угла (точки), а также ширины и длины). Это представление задается так:

(define (make-rectangle topleft width height) 
  (cons topleft (cons width height)))
(define (rectangle-topleft rectangle) 
  (car rectangle))
(define (rectangle-width rectangle) 
  (car (cdr rectangle)))
(define (rectangle-height rectangle) 
  (cdr (cdr rectangle)))

Как видим, процедуры вычисления периметра и площади остаются неизменными при переходе от одного представления к другому. Они не изменятся и в случае, когда мы будем использовать более сложные представления для прямоугольников, ориентированных на плоскости произвольным образом.

Comments

Comment from gorilych
Date: July 17, 2008, 2:02 pm

Как я понял, смена представления не должна затрагивать интерфейсов основных функций, а здесь меняется набор и значения параметров для процедуры make-rectangle

Тем не менее, основное требование (“одни и те же процедуры вычисления периметра и площади работали с любым из Ваших представлений”) выполнено ибо rectangle-perimeter и rectangle-area не используют конструктор.

Comment from eugene0
Date: January 21, 2009, 9:53 am

На мой взгляд, это не очень хорошее решение, т.к. ваши определения не могут сосуществовать в одной системе, а значит практической пользы от такого полиморфизма нет. Я бы сделал так:


(define (make-recatngle1 topleft bottomright)
(cons (topleft bottomright))

(define (make-rectangle2 topleft width height)
(cons (topleft
(make-point (+ (x-point topleft) width) (+ (y-point topleft) height)))

Остальное так же, как у вас в первом варианте представления. Теперь остальные функции для работы с прямоугльниками будут едиными, т.к. внутреннее представление одно, несмотря на то, что сконструировать его можно двумя способами.

Comment from alchimik
Date: September 4, 2013, 8:44 am

Теперь остальные функции для работы с прямоугольниками будут едиными, т.к. внутреннее представление одно, несмотря на то, что сконструировать его можно двумя способами.

Lol. Задание как раз и состоит в том, чтобы сделать разные представления

Comment from Irv
Date: January 20, 2016, 4:36 pm

Барьеры абстракций:

(define (square x) (* x x))

(define (make-point x y) (cons x y))
(define (x-point p) (car p))
(define (y-point p) (cdr p))

(define (segment-length point-a point-b)
  (let ((x1 (x-point point-a))
        (x2 (x-point point-b))
        (y1 (y-point point-a))
        (y2 (y-point point-b)))
    (sqrt (+
           (square (- x2 x1))
           (square (- y2 y1))))))

(define (rectangle-perimeter r)
  (let ((a (rectangle-width r))
        (b (rectangle-height r)))
    (* 2 (+ a b))))

(define (rectangle-square r)
  (let ((a (rectangle-width r))
        (b (rectangle-height r)))
    (* a b)))

Представление прямоугольника через три точки

(define (make-rectangle point-a point-b point-c)
  (cons point-a
        (cons point-b point-c)))

(define (a-point r) (car r))
(define (b-point r) (car (cdr r)))
(define (c-point r) (cdr (cdr r)))

(define (rectangle-width r)
  (segment-length (a-point r) (b-point r)))
(define (rectangle-height r)
  (segment-length (b-point r) (c-point r)))

(define r (make-rectangle
           (make-point 0 0)
           (make-point 20 0)
           (make-point 20 10)))

(rectangle-perimeter r)
(rectangle-square r)

Представление прямоугольника через две точки и высоту

(define (make-rectangle point-a point-b height)
  (cons height
        (cons point-a point-b)))

(define (a-point r) (car (cdr r)))
(define (b-point r) (cdr (cdr r)))

(define (rectangle-width r)
  (segment-length (a-point r) (b-point r)))
(define (rectangle-height r) (car r))

(define r (make-rectangle
           (make-point 0 0)
           (make-point 20 0)
           10))

(rectangle-perimeter r)
(rectangle-square r)

Write a comment