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

27 February, 2008 (20:25) | Решения упражнений

Идея Хьюго о добавлении процедур приведения типов к самим себе, скажем так, не слишком светлая.

а. Посмотрим, что происходит при вызове обобщенной процедуры exp, определенной только для обычных чисел, с двумя комплексными параметрами в случае, когда заданы процедуры приведения типов к себе, предложенные Хьюго. Сначала exp вызовет apply-generic для операции exp и двух комплексных чисел. apply-generic не найдет подходящую обобщенную процедуру по этой операции и типам и попытается привести типы аргументов. У нее получится осуществить приведение типа первого аргумента к типу второго (complex к complex), а следоавтельно она рекурсивно запустит себя же для тех же типов. Мы пришли к тому же, с чего начинали. Таким образом произойдет зацикливание. Это явно не то, что нам нужно.

б. Получается, предложенным Хьюго способом проблему не решить. Что же делать? Прежде всего, определим, существует ли вообще проблема. В каком случае может потребоваться приведение типа к себе же? Только в случае вызова apply-generic с однотипными аргументами, причем в ситуации, когда процедура для этого типа не задана. Если процедуры приведения типа к себе нет, то особой беды не произойдет, разве что может пострадать эффективность. А вот если кто-то добавит такую процедуру приведения, то наша система может встретиться с серьезными трудностями, например зацикливанием, как показано выше. Было бы более безопасно, если бы apply-generic не пыталась привести тип к самому себе в принципе.

в. Измененный вариант процедуры apply-generic, не выполняющий приведение типов в случае, когда типы операндов совпадают, представлен ниже:

(define (apply-generic op . args)
  (let ((type-tags (map type-tag args)))
    (let ((proc (get op type-tags)))
      (if proc
          (apply proc (map contents args))
          (if (= (length args) 2)
              (let ((type1 (car type-tags))
                    (type2 (cadr type-tags))
                    (a1 (car args))
                    (a2 (cadr args)))
                (if (eq? type1 type2)
                    (error "Нет метода для этих типов"
                           (list op type-tags))
                    (let ((t1->t2 (get-coercion type1 type2))
                          (t2->t1 (get-coercion type2 type1)))
                      (cond (t1->t2
                             (apply-generic op (t1->t2 a1) a2))
                            (t2->t1
                             (apply-generic op a1 (t2->t1 a2)))
                            (else
                             (error "Нет метода для этих типов"
                                    (list op type-tags)))))))
              (error "Нет метода для этих типов"
                     (list op type-tags)))))))

Реально я добавил лишь одну строчку проверки условия равенства типов (eq? type1 type2) и сообщение об ошибке.

Write a comment