2.5 System with Generic Operations

제네릭 연산의 체계


하나의 데이터 객체를 다른 방법으로 표현하여 디자인하는 시스템을 살펴보았다. 

중요한 아이디어는, 제네릭 인터페이스 프로시저들을 통해, 데이터 연산들을 몇몇의 표현에 연결하는 것이었다. 

이제 우리는 이러한 아이디어를 다른 표현에 이용하는데 그치지 않고 다른 인자들에도 접목하도록 한다.

우린 산술 연산들의 다양한 패키지를 이미 살펴보았다: 기본 산술 연산자들, (+, -, *, /) 

유리수 산술 연산 (add-rat, sub-rat, mul-rat, div-rat), 복소수 산술 연산들이 바로 그 것.


우리는 이제 데이터기반 테크닉을 산술연산의 패키지를 생성하는데 사용할 것이다.


누군가 "숫자"를 산다는 관점에서, add 연산이 제공된다. ADD 는 제네릭인터페이스의 일부인데, 별개의 일반적인 산술, 유리수 산술, 복소수 산술 패키지들이 일관된 형태로 접근 된다. 

어떤 별개의 산술연산 패키지는 제네릭 프로시져를 통해 접근되며, 이 프로시져는 패키지를 다른 표현으로 디자인된 패키지에 결합 (retangluar / polar ) 한다. 더 나아가, 이 시스템은 추가가 간편해서, 개별의 산술 패키지를 추가하고 제네릭 산술 시스템을 생성하도록 결합하는 것이 가능하다. 


2.5.1 제네릭 산술연산 

제네릭 산술 연산을 디자인 하는것은 제네릭 복소수 연산을 만드는 것과 유사하다. 예를 들면, 제네릭 프로시저 add 는 일반적인 수에는 + 를 적용하고, 유리수에는 add-rat 을 동작케하는 것과 같다. 다른 제네릭 산술 연산을 추가하기위해 2.4.3 에서 보았던 복소수를 위한 제네릭 선택자를 구현하는 것과 같은  전략을 사용하면 된다.

tag 를 추가하고 제네릭 프로시져가 적절한 패키지를 그것의 인자에 따라 디스패치하도록 한다.



제네릭 산술 연산은 다음과 같이 정의 된다.


(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))


일반 수에 대해서부터 시작해보자. tag 는 scheme-number


(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))


제네릭산술체계의 틀이 잡혔으므로, 쉽게 새로운 종류의 수를 추가할 수 있다. 

추가가 손쉽다는 이점을 살려, 2.1.1 의 유리수 코드의 교체 없이 추가할수 있다.


(define (install-rational-package)

;; internal procedures

(define (numer x) (car x))

(define (denom x) (cdr x))

(define (make-rat n d)

(let ((g (gcd n d)))

(cons (/ n g) (/ d g))))

(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))))

;; interface to rest of the system

(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))


유사한 패키지를 complex 태그를 사용하여 복소수를 다루는데 추가할 수 있다. 


(define (install-complex-package)

;; imported procedures from rectangular and polar packages

(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))

;; internal procedures

(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 (* (magnitude z1) (magnitude z2))

  (+ (angle z1) (angle z2))))

(define (div-complex z1 z2)

(make-from-mag-ang (/ (magnitude z1) (magnitude z2))

(- (angle z1) (angle z2))))

;; interface to rest of the system

(define (tag z) (attach-tag 'complex z))

(put 'add '(complex complex)

(lambda (z1 z2) (tag (add-complex z1 z2))))

(put 'sub '(complex complex)

(lambda (z1 z2) (tag (sub-complex z1 z2))))

(put 'mul '(complex complex)

(lambda (z1 z2) (tag (mul-complex z1 z2))))

(put 'div '(complex complex)

(lambda (z1 z2) (tag (div-complex z1 z2))))

(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)


이 패키지 외부의 프로그램은 복소수를 rectangluar form / ploarform 모두를 사용할 수 있다. 

바탕이 되는 프로시져가 (실제로는 어느한쪽의 패키지에 정의되어있는) 복소수 패키지 바깥으로 보여지고 있으며, 외부세계로 exported 하고 있다. 


(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))


여기서는 2단계의 태그 시스템을 가지고 있다. 어떤 표현방식 ( rec / pol ) 인지, 어떤 수인지 ( complex ) .

크고 복잡한 시스템에서는 많은 단계를 가지고 있을수 있으며, 다음을 위한 인터페이스들은 제네릭연산으로 인터페이스를 갖게 된다. 데이터 객체들이 "하위로" 전달됨에 따라, 바깥의 태그는 적절한 패키지로 방향을 잡고 contents 를 정요하여  이를 떼어내어 다음 단계의 태그가 앞으로의 dispatch 대상이 되도록 한다.


위의 패키지에서 add-rat / add-complex 를 완전히 예전에 썼던 코드를 그대로 사용하였다. 일단 각 인스톨 프로시져의 내부로 들어가게되면, 더이상 각각을 구분하기위한 이름은 필요없으므로, 단순한 이름을 사용하여도 된다. 




'SICP > 2. Building Abstractions with Data' 카테고리의 다른 글

2.5.3 Example: Symbolic Algebra  (0) 2015.11.01
2.3.4 Huffman Encoding Trees  (3) 2015.08.05
2.1.3 데이터란 무엇인가 ?  (0) 2015.02.07