복소수 하나는 서로 유용한 두 좌표계를 가질 수 있다.
직교형 내용은 덧셈을 직접적으로 만들고 극형 내용은 곱셈을 직접적으로 만듭니다. 하나의 태그 선택자 인터페이스는 어느 표현이든 같은 추상 복소수로 관찰하게 합니다.
복소수 인터페이스 하나가 직교형과 극형 표현의 장점을 동시에 보존하려면 어떻게 해야 할까요?
- 실수부와 허수부로 직교형 내용 만들기
- 크기와 각도로 극형 내용 만들기
- 표현 태그를 통해 공통 선택자 디스패치하기
- 덧셈에는 직교 좌표를, 곱셈에는 극 좌표를 사용하기
- 부동소수점 삼각함수 관찰을 명시적인 허용 오차로 비교하기
직교형 객체는 x와 y를 저장하므로 real-part와 imag-part는 바로 읽고 크기와 각도는 계산합니다. 극형 객체는 r과 a를 저장하므로 크기와 각도는 바로 읽고 실수부와 허수부는 계산합니다. 바깥 태그는 표현에 대한 정보이며 클라이언트 코드가 보는 추상 복소수의 일부는 아닙니다.
첫 프로그램은 3 + 4i를 두 표현으로 각각 만들고 같은 네 선택자로 관찰합니다. close?는 삼각함수를 거친 부동소수점 값이 선언한 허용 오차 안에서 일치한다는 사실을 기록합니다. 두 번째 프로그램은 실수부와 허수부를 더해 직교형 결과를 만들고, 크기를 곱하고 각도를 더해 극형 결과를 만듭니다. 공통 선택자는 어느 결과에서도 내부 내용을 노출하지 않고 추상적인 값을 확인합니다.
(begin
(define (square x) (* x x))
(define (close? left right)
(< (abs (- left right)) 0.000001))
(define (attach-tag type contents) (cons type contents))
(define (type-tag object) (car object))
(define (contents object) (cdr object))
(define (make-from-real-imag x y)
(attach-tag 'rectangular (list x y)))
(define (make-from-mag-ang magnitude angle)
(attach-tag 'polar (list magnitude angle)))
(define (rectangular-real z) (car z))
(define (rectangular-imag z) (cadr z))
(define (rectangular-magnitude z)
(sqrt (+ (square (rectangular-real z))
(square (rectangular-imag z)))))
(define (rectangular-angle z)
(atan (rectangular-imag z) (rectangular-real z)))
(define (polar-magnitude z) (car z))
(define (polar-angle z) (cadr z))
(define (polar-real z)
(* (polar-magnitude z) (cos (polar-angle z))))
(define (polar-imag z)
(* (polar-magnitude z) (sin (polar-angle z))))
(define (select object rectangular-selector polar-selector)
(cond ((eq? (type-tag object) 'rectangular)
(rectangular-selector (contents object)))
((eq? (type-tag object) 'polar)
(polar-selector (contents object)))
(else (error "unknown complex representation"))))
(define (real-part z) (select z rectangular-real polar-real))
(define (imag-part z) (select z rectangular-imag polar-imag))
(define (magnitude z)
(select z rectangular-magnitude polar-magnitude))
(define (angle z) (select z rectangular-angle polar-angle))
(define rectangular (make-from-real-imag 3.0 4.0))
(define polar (make-from-mag-ang 5.0 (atan 4.0 3.0)))
(list (type-tag rectangular)
(type-tag polar)
(close? (real-part rectangular) (real-part polar))
(close? (imag-part rectangular) (imag-part polar))
(close? (magnitude rectangular) (magnitude polar))
(close? (angle rectangular) (angle polar))))- 출력
- —
- 값
- —
- 진단
- —
첫 프로그램은 (rectangular polar #t #t #t #t)를 반환합니다. 두 번째 프로그램은 (rectangular polar #t #t #t #t #t #t)를 반환합니다.
각 선택자가 직교형 또는 극형 내용을 고르기 전에 바깥 type tag를 읽는 과정을 따라가세요. 첫 실행에서는 직접 읽는 선택자와 sqrt, atan, sin, cos를 거치는 경로를 비교합니다. 두 번째 실행에서는 덧셈이 직교형 내용을 만들고 곱셈이 극형 내용을 만든 뒤 같은 공통 선택자가 두 결과를 확인하는지 보세요. 허용 오차는 이번 유한한 부동소수점 계산만 설명하며 임의의 수치 프로그램에 대한 동등성 증명은 아닙니다.
힌트를 보기 전에 프로그램을 바꿔 보세요.
-3 + 4i를 크기 5와 각도 (atan 4.0 -3.0)을 사용해 두 표현으로 각각 만드세요. close?로 네 선택자를 확인한 뒤 첫 사분면 밖에서 atan의 두 인자가 필요한 이유를 설명하세요.
힌트 하나 보기
두 좌표의 부호가 사분면을 결정합니다. y를 x로 나눈 값에 한 인자 atan만 적용하면 그 구분을 잃습니다.