2019년 8월 25일 일요일

[on lisp] 6.2 Compiling Network (클로저이용 중요)

6.1에서 사용한 전통적인 방식은 어느 프로그래밍 언어로도 가능하다.
우리는 하지만 더 나은 방법을 제시한다.

Fig 6.5를 보자.
해당 자료구조를 위한 노드를 가지고, 순회하기 위한 분리된 코드를 가지는 대신! 우리는 클로저로 노드를 표현한다.
run-node같은 것은 이제 필요없다. 모두 노드 안에 들어가게 된다.
새로운 코드를 보기 전에 이전 방식을 떠올려보자.
순회를 시작하려면 어디부터 시작할 곳을 꺼내서 실행하면 된다.
(funcall (gethash 'people *nodes*)) 
Is the person a man? 
>>
이제 클로저로 노드를 표현하게 되면, 우리는 20개의 질문 네트워크 전부를 코드 안에 넣을 수 있게 된다.
지금 현재 코드는 런타임에 노드 함수를 이름에 따라서 찾아(look-up)야 한다.
하지만 만약 이 네트워크가 그때그때 재정의 되지 않는다고 한다면! 우리는 좀 더 개선할 수 있게 된다.
* 이제 해시테이블을 거치지 않고 직.접. 목적지로 가게 하는 노드함수를 만드는 것.
(defvar *nodes* nil)

(defun defnode (&rest args) 
  (push args *nodes*) args)

(defun compile-net (root) 
  (let ((node (assoc root *nodes*))) 
    (if (null node) 
        nil 
        (let ((conts (second node)) 
              (yes (third node)) 
              (no (fourth node))) 
          (if yes 
            (let ((yes-fn (compile-net yes)) 
                  (no-fn (compile-net no))) 
              #'(lambda () (format t "~A~%>> " conts) 
                               (funcall (if (eq (read) ’yes) 
                                             yes-fn 
                                             no-fn)))) 
            #'(lambda () conts))))))
Figure 6.6: Compilation with static references.

When the original call to compile-net returns, it will yield a function representing the portion of the network we asked to have compiled.

그림6.6이 새로운 버전이다. 이제 *nodes*는 계속 사용되는 hast-table이 아닌 일회용의 리스트이다.
모든 노드는 defnode로 먼저 정의된다, 하지만 지금 클로저가 생기는 것은 아니다.
노드가 모두 정의된 다음에 compile-net함수를 실행하여 모든 네트워크를 한번에 컴파일 한다.

이 함수는 트리의 리프까지 재귀적으로 작동하며 백업(돌아올라오는) 도중에 각 단계에서 두 개의 서브트리 각각에 대한 노드/함수를 반환합니다.
1. 하여 각 노드는 두개의 노드를 아예 다룰 수 있도록 자체를 가지고 있게 된다. (목적지 이름만 가지고 있는 것이아니라.)
원래 compile-net에 대한 호출이 리턴하면 이 값은 네트워크 자체를 표현하는 함수가 만들어진 것이다.(모든 네트워크 연결을 클로저로 완성한)
> (setq n (compile-net ’people)) 
# 
> (funcall n) 
Is the person a man? 
>>
compile-net은 두가지 의미로 컴파일 되는 것이다.
1. 일반적인 의미로는, 추상적인 네트워크 표현을 코드로 변환한다.
2. 더 나아가서, compile-net 자체가 컴파일 되면, 이녀석은 컴파일된 함수를 리턴할 것이다.(See page 25)
그러니까 내용물을 컴파일 하냐, compile-net 코드 자체냐

네트워크를 컴파일한 이후에는, defnode로 만든 리스트는 필요없다.
nil로 덮어서 가비지 컬렉터가 묶여있는 메모리를 풀 수 있다.

6.3 Looking forward


네트워크를 포함하는 많은 프로그램들은 이렇게 노드들을 클로저에 담는 방식으로 컴파일하여 구현할 수 있다.
클로저는 데이터 객체들이다, 그리고 자료구조가 사물을 표현하는 것또한 할 수 있다.
이렇게 하기 위해선 약간 관습적인 사고(훈련)이 필요하지만, 그 보상은 더 빠르고 우아한 프로그램이다.

Macros help substantially when we use closures as a representation.
매크로는 클로저를 표현하는데 실질적인 도움이 된다.
“To represent with closures” is another way of saying “to compile,” and since macros do their work at compile-time, they are a natural vehicle for this technique.
"클로저로 표현하기"는 "컴파일하기"라는 작업의 또 다른 방식이며, 매크로는 일 자체를 컴파일 타임에 하기 때문에, 이 기술을 위한 자연스러운 수단이다.
After macros have been introduced, Chapters 23 and 24 will present much larger programs based on the strategy used here.
매크로에 대해 소개를 한뒤, 챕터 23, 24에서 이제 소개될 전략에 대해서 더 큰 프로그램을 재현할 것이다.

댓글 없음:

댓글 쓰기