Решение упражнения 2.83 из SICP
Начнем с обобщенной процедуры 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