오상우
오상우

Categories

  • lisp
  • scheme
  • sicp

지난 포스트에서 다룬 복소수 산술 연산과 더 이전에 다룬 유리수 산술 연산, lisp에 존재하는 기본 산술 연산을 활용해 일반화된 산술 연산을 설계하여 보자.
우선 일반화된 산술 프로시저를 다음과 같이 정의하자.

(define (add x y) (apply-generic 'add x y))
(define (sub x y) (apply-generic 'sub x y))
(define (mul x y) (apply-generic 'mul x y))
(define (div x y) (apply-generic 'div x y))

그리고 타입/연산 테이블에 기본 산술 연산 package부터 집어넣자.

(define (install-scheme-number-package)
  (define (tag x) (attach-tag 'scheme-number x))
  (put 'add '(scheme-number scheme-number)
       (lambda (x y) (tag (+ x y))))
  (put 'sub '(scheme-number scheme-number)
       (lambda (x y) (tag (- x y))))
  (put 'mul '(scheme-number scheme-number)
       (lambda (x y) (tag (* x y))))
  (put 'div '(scheme-number scheme-number)
       (lambda (x y) (tag (/ x y))))
  (put 'make 'scheme-number
       (lambda (x) (tag x)))
  'done)

그리고 전역에 scheme-number 타입 수의 생성자를 정의한다.

(define (make-scheme-number n)
  ((get 'make 'scheme-number) n))

이렇게 기존의 코드를 수정하지 않고도 쉽게 새로운 함수를 추가할 수 있다.
그리고 우리가 유리수_포스트 에서 작성했던 코드를 그대로 package로 추가할 수 있다.

(define (install-rational-package)
  ; 로컬 프로시저
  (define (make-rat x y)
    (let ((gcd-val (gcd x y)))
      (if (> 0 (* x y))
          (cons (- 0 (abs (/ x gcd-val))) (abs (/ y gcd-val)))
          (cons (abs (/ x gcd-val)) (abs (/ y gcd-val))))))
  (define (numer x) (car x))
  (define (denom x) (cdr x))
  (define (add-rat x y)
    (make-rat (+ (* (numer x) (denom y)) (* (numer y) (denom x)))
              (* (denom x) (denom y))))
  (define (sub-rat x y)
    (make-rat (- (* (numer x) (denom y)) (* (numer y) (denom x)))
              (* (denom x) (denom y))))
  (define (mul-rat x y)
    (make-rat (* (numer x) (numer y))
              (* (denom x) (denom y))))
  (define (div-rat x y)
    (make-rat (* (numer x) (denom y))
              (* (denom x) (numer y))))
  
  ; 인터페이스 표에 put
  (define (tag x) (attach-tag 'rational x))
  (put 'add '(rational rational)
    (lambda (x y) (tag (add-rat x y))))
  (put 'sub '(rational rational)
    (lambda (x y) (tag (sub-rat x y))))
  (put 'mul '(rational rational)
    (lambda (x y) (tag (mul-rat x y))))
  (put 'div '(rational rational)
    (lambda (x y) (tag (div-rat x y))))
  (put 'make 'rational
    (lambda (n d) (tag (make-rat n d))))
  'done)

; 전역에 유리수 데이터 생성자 추가
(define (make-rational n d)
  ((get 'make 'rational) n d))

마지막으로 복소수 package를 정의하자. 이때, 지난_포스트에서 정의한 내용들은 이미 적용되어 있다고 생각한다.

(define (install-complex-package)
  ; 직각 좌표/극 좌표 package에서 가져온 constructor 프로시저
  (define (make-from-real-imag x y)
    ((get 'make-from-real-imag 'rectangular) x y))
  (define (make-from-mag-ang r a)
    ((get 'make-from-mag-ang 'polar) r a))
  
  ; 로컬 프로시저
  (define (add-complex z1 z2)
    (make-from-real-imag (+ (real-part z1) (real-part z2))
                         (+ (imag-part z1) (imag-part z2))))
  (define (sub-complex z1 z2)
    (make-from-real-imag (- (real-part z1) (real-part z2))
                         (- (imag-part z1) (imag-part z2))))
  (define (mul-complex z1 z2)
    (make-from-mag-ang (* (real-part z1) (real-part z2))
                       (+ (imag-part z1) (imag-part z2))))
  (define (div-complex z1 z2)
    (make-from-mag-ang (/ (real-part z1) (real-part z2))
                       (- (imag-part z1) (imag-part z2))))
  
  ; 표에 인터페이스 put
  (define (tag x) (attach-tag 'complex x))
  (put 'add '(complex complex)
    (lambda (x y) (tag (add-comlex x y))))
  (put 'sub '(complex complex)
    (lambda (x y) (tag (sub-comlex x y))))
  (put 'mul '(complex complex)
    (lambda (x y) (tag (mul-comlex x y))))
  (put 'div '(complex complex)
    (lambda (x y) (tag (div-comlex x y))))
  (put 'make-from-real-imag 'complex
    (lambda (x y) (tag (make-from-real-imag x y))))
  (put 'make-from-mag-ang 'complex
    (lambda (r a) (tag (make-from-mag-ang r a))))
  'done)

; 전역에 복소수 생성자 두개 추가
(define (make-complex-from-real-imag x y)
  ((get 'make-from-real-imag 'complex) x y))
(define (make-complex-from-mag-ang r a)
  ((get 'make-from-mag-ang 'complex) r a))

복소수 데이터의 경우, tag가 두개가 붙어 있게 된다. 먼저 complex 태그가 붙어있고, 그 태그를 떼어내면 더 아래 레벨에 직각/극 좌표 방식을 구분해주는 태그가 있다.

연습문제 2.77

샤실 위의 복소수 코드는 아직 불완전하다. (magnitude z)를 실행해 보면 magnitude / complex 에 맞는 연산이 표에 없다는 에러가 발생한다. 실제로 우리는 complex에 쓰일 selector 연산을 표에 집어넣은 적이 없다. 아래 코드를 complex package에 집어넣으면 에러가 해결된다.

(put 'real-part '(complex) real-part)
(put 'imag-part '(complex) imag-part)
(put 'magnitude '(complex) magnitude)
(put 'angle '(complex) angle)