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

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

Начнем с обобщенной процедуры raise, которая будет поднимать объект на один уровень в башне типов. Она записывается абсолютно аналогично остальным обобщенным операциям:

(define (raise x) (apply-generic 'raise x))

Просто. Но для того, чтобы она заработала, необходимо определить конкретные реализации подъема типа в башне для всех типов, кроме последнего, самого верхнего в иерархии. Мне кажется, что помещать соответствующие процедуры подъема объекта типа А к находящемуся непосредственно над ним в башне типу В лучше в пакете, описывающем тип В, так как именно более общий тип должен заботиться о приведении к себе частных. Впрочем, возможно, есть и лучшее решение.

Итак, в пакет для работы с целыми числами (integer) мы не включаем ничего.

В пакет для работы с рациональными числами (rational) включаем строки:

(define (integer->rational n)
  (make-rational n 1))
(put 'raise '(integer) integer->rational)

В пакет для работы с действительными числами (real) включаем:

(define (rational->real r)
  (make-real (/ (numer r) (denom r))))
(put 'raise '(rational) rational->real)

Пакет комплексных чисел (complex) дополняем так:

(define (real->complex x)
  (make-complex-from-real-imag x 0))
(put 'raise '(real) real->complex)

Все. Теперь подъем по иерархии типов с помощью raise будет работать.

Comments

Pingback from SICP по-русски » Решение упражнения 2.85 из SICP
Date: March 5, 2008, 10:06 pm

[…] Определение операции проецирования project похоже на определение операции raise. […]

Comment from thror
Date: May 6, 2009, 9:47 pm

и… Оказывается все это не работает:) Теперь объясню почему.

“…так как более общий тип должен заботиться о приведении к себе частных…”-это что, из книги-бреда типа буча или рамбо? Не слушайте вы их.

Сначала в общем объясню. Потом перейду к частностям. Итак. На самом деле процесс “поднятия” или “приведения” типа T к надтипу Х состоит из двух этапов, условно “анализ” и “синтез”. Под анализом мы понимаем выделение существенно важных составных частей цельного объекта типа Т. Под синтезом-непосредственно конструирование из них объекта надтипа Х. Существенно то, что фаза анализа должна проводиться той компонентой системы, которая знает о внутреннем устройстве типа Т-непосредственно или через экспортируемые селекторы…

Но беда вот в чем-поскольку приведение raise будет обычной обобщенной процедурой, внутри нее объект типа Т будет уже не помеченым тэгом типа-вспомните как работает apply-generic. Значит экспорт селекторов нам не поможет.

Вывод один-raise должна определяться внутри пакета для приводимого типа Т, и использовать его внутренние селекторы. Внутренние. И еще раз повторю-внутренние. И экспортируемый конструктор типа Х.

(возникает вопрос-что за numer и denom фигурируют у автора? Если обобщенные-то они не будут работать при такой схеме, ведь у числа передаваемого им уже не будет тега. Если внутренние для пакета рациональных чисел, то это нарушение принципа абстракции)

Comment from thror
Date: May 6, 2009, 9:56 pm

то есть, до тех пор, пока мы не вызывали raise при помощи apply-generic, мы могли помешать ее куда угодно, и пользоваться в ней экспортируемыми селекторами, позволяя им снимать тэг типа.

Но как только мы вызываем raise при помощи apply-generic, следует помнить, что тэг типа внутри raise будет уже снят, и обобщенными экспортированными селекторами нам уже не воспользоваться.

Comment from thror
Date: May 24, 2009, 9:11 pm

есть также отличный аргумент против того, чтоб raise была определена через apply-generic.

Предположим, что raise вызывает apply-generic, как советует автор. Но тогда в случае если raise не определена для типа ее аргумента, она будет пытаться привести аргумент к подходящему типу (как следует из кода apply-generic) то есть “поднять” свой аргумент при помощи той же raise-и конечно ее войдет в бесконечный цикл.

Разрешить этот вопрос мы можем лишь усложнив определение raise, добавив соответствующие проверки,но поверьте, после экспериментов я выяснил, что лучше уж в таком случае вообще никак не определять raise, а работать с ней в таком стиле

(let ((v (get-coercion 'raise (type-tag arg))))
(if v
...
...))

Write a comment