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

7 March, 2008 (20:59) | Решения упражнений

Сказать, какой смысл определять комплексные числа с действительными и мнимыми частями, не являющимися действительными числами, мне трудно. Тем не менее, легко представить, что в системе существует несколько различных представлений действительных чисел (например, различающихся точностью и объемом требуемой памяти). В таком случае задача имеет смысл.

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

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

;install-complex-package
(define (add-complex z1 z2)
  (make-from-real-imag (add (real-part z1) (real-part z2))
                       (add (imag-part z1) (imag-part z2))))
(define (sub-complex z1 z2)
  (make-from-real-imag (sub (real-part z1) (real-part z2))
                       (sub (imag-part z1) (imag-part z2))))
(define (mul-complex z1 z2)
  (make-from-mag-ang (mul (magnitude z1) (magnitude z2))
                     (add (angle z1) (angle z2))))
(define (div-complex z1 z2)
  (make-from-mag-ang (div (magnitude z1) (magnitude z2))
                     (sub (angle z1) (angle z2))))
;install-rectangular-package
(define (magnitude z)
  (sqrt (add (square (real-part z))
             (square (imag-part z)))))
(define (make-from-mag-ang r a)
  (cons (mul r (cosine a)) (mul r (sine a))))
;install-polar-package
(define (real-part z)
  (mul (magnitude z) (cosine (angle z))))
(define (imag-part z)
  (mul (magnitude z) (sine (angle z))))
(define (make-from-real-imag x y)
  (cons (sqrt (add (square x) (square y)))
        (arctan y x)))

Обратите внимание, на то, что имена тригонометрических процедур изменились. Это неспроста!

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

Операции, которые требуется сделать обобщенными:

  • square
  • sqrt
  • sin
  • cos
  • atan

На самом деле square и sqrt можно и не делать обобщенными операциями, а просто заменить в их определениях арифметические операции на обобщенные. Например, так:

(define (square x)
  (mul x x))
(define (average x y)
  (div (add x y) 2.0))
(define (sqrt x)
  (fixed-point (average-damp (lambda (y) (div x y)))
               1.0))

Однако при этом константы 1.0 и 2.0 (или любые другие типизированные константы) вынудят систему приводить типы.

Описывать реализацию sin, cos и atan как обобщенных операций полностью я не буду. Она абсолютно аналогична ранее введенным обобщенным операциям. Так же добавляются определения обобщенных процедур (они будут называться несколько иначе: sine, cosine и arctan) через apply-generic и конкретные процедуры для каждого типа добавляются с помощью put в таблицу типов в каждом пакете. Для обычных чисел это будет выглядеть так:

;generic functions
(define (sine x) (apply-generic 'sine x))
(define (cosine x) (apply-generic 'cosine x))
(define (arctan x) (apply-generic 'arctan x))
;install-scheme-number-package
(put 'sine '(scheme-number) (lambda (x) (tag (sin x))))
(put 'cosine '(scheme-number) (lambda (x) (tag (cos x))))
(put 'arctan '(scheme-number scheme-number) (lambda (x y) (tag (atan x y))))

Для других типов все аналогично и выписать реализацию несложно.

Write a comment