2010.01.09 18:14

taoi part0 session 3

게으름을 피우다가는 올해도 다 갈 것 같아 키보드 놀이를 해보기로 했다. 
동아가는 소스코드를 만들어야 하는 과제다.  키보드로 입력하고 고치는 것이 적성에는 맞지 않지만 만들다보면 많은 것을 생각하게 한다.
재구성이 아니라 처음부터 만드는 것이면 정말 집중할 수 있을 것 같다.  요즘같으면 머리 돌기에 방해가 되는 담배를 피우지 않았으면 하고 바란다.  금연을 하면  더 오래 키보드 앞에 붙어있기 때문이며 덜 귀찮아 한다. 
아무튼 입력을 했고 조금씩 더 환경다운  환경으로 변해갈 것이다.  

----------------------------------------------------------------

TAOI의 7 페이지에는 드라이버루프가  다음과 같은 프로시저 정의를 읽고 저장하는 경우를 다루고 있다:

(define  (f a b c ..)  <expression in A B C .. and F G H..>)

드라이버루프는 이미 정의된 프로시저에 인자를  적용하여 값을 계산(evaluate)할 수도 있다. 정의되는 프로시저는 정의된 다른 프로시저나 처음부터 정의된 원시프로시저 (primitive procedure)를 참조할 수 있다.  만약 계산이 일어나는 시점에 필요한 정의내용들을 구할 수 있다면 프로시저 정의는 미리참조(forward reference)를 포함 할 수 있다. 인터프리터도 이런 방식으로 정의된다. 

TAOI에서 다루려고 하는 언어는 applicative order 다.  프로시저에 적용될 인자들은 미리 모두 완전히 계산되어야 한다. (이점을 분명히 해둘 필요가 있다고 저자들은 적고 있다.)

지난번에 소개한 Part 0의 session1에 나온 driver loop를 돌려보자.
몇 개는 귀찮아서 코드를 조금 변경했다. 이글을 쓸 당시와 달라진 프로시저나 함수들도 있다. 

Fig 3은 유틸리티 프로시저로 이들을 돌리기 위해 조금 변경했다. 
다음과 같은 것들을 정의해 보자: 

(define (bind vars vals base-env)
  (if (= (length vars) (length vals))
      (cons (cons  vars vals) base-env)
      (if (< (length vars) (length vals))
          (error "Too many arguments supplied" vars vals)
          (error "Too few arguments supplied" vars vals))))

(define (value name env)
  (value1 name (lookup name env))
    )

(define (value1 name slot )
  ( cond ((eq? slot '&UNBOUND) (display 'error))
         (else (car slot))
   )
  )

(define (lookup name env)
  (cond ((null? env ) '&UNBOUND)
        (else (lookup1 name (caar env) (cdar env) env))))
  
(define (lookup1 name vars vals env)
  
      (cond ((null? vars)
             (lookup name (cdr env) ))
            
            ((eq? name (car vars)) vals)
            (else (lookup1 name (cdr vars) (cdr vals) env))))
    


(define (driver)
  (driver-loop '() (display '|LITHP ITH LITHTENING|))
  )

(define (driver-loop procedures hunoz)
  (driver-loop-1 procedures (read)))

(define (driver-loop-1 procedures form  )
  (cond (
         ;(atom form )         (drive-loop procedures (print (eval form '() procdures))))
        ((eq? (car form ) 'define)
         (driver-loop (bind (list (caadr form ))
                             (list (list (cdadr form) (caddr form ))) 
                                   procedures)
                      (display (caadr form ))))
        (t (driver-loop procedures (print (eval form '() procdures)))) 
        
     )
 )
)
 
지난번의 파트0 세션 2와 비교해보자. 조금 다르다는 것을 알수 있다. 그러나 기능은 같은 것이다. (http://toyfab.tistory.com/entry/TAOI-Part-0-session-2   )

첫번째로 변수가 저장되는 공간 환경은 '()라고 볼 수 있다. 

(bind '(a) '(10) '())
==>(((a) 10))

 (bind '(a d) '(10 100) '())
==>(((a d) 10 100)

이 환경에  변수 b의 값을 정의해보자. 새로운 환경이라고 할 수 있다.

(bind '(b) '(10) '(((a d) 10 100)))
==>(((b) 10) ((a d) 10 100))

지난번 bind와의 차이를 알아낼 수 있겠는가?  그 차이는 그림으로 cons 하는 구조가 다르다는 것에서 알 수 있다. 
사소한 차이지만 종이로 그려보면 아주 쉽다. 

이제 lookup 과 관련된 부분을 테스트해보자  plt 스킴의 디버거의 위력을 시험해 볼 수 있는 순간이다. 스텝 동작이 가능하다. 
앞에서 만든 환경을 테스트해볼 수 있는 부분이다. 

 (value 'a       '(((b) 10) ((a d) 10 100)))
==> 10
 (value 'd      '(((b) 10) ((a d) 10 100)))
==>100

 
그리고 원래의 소스를 조금 고치긴 했지만 그럭저럭 돌아간다. 아직 eval()은 돌아가지 않는다. (돌아간다는 말은 예전 같으면 수행된다는 용어를 사용했을 것이나 SICP번역후 돌아간다는 말을 사용하게 되었다. 어느것이 더 좋은지는 모르나 이런 것은 습관이다.)

이제 plt-스킴 r5rs 환경에서 

(driver) 라고 치면 입력창이 나타난다.
프롬프트는 LITHP ITH LITHTENING 이다.
다음과 같은 입력을 해보자. 함수 f 는 a 와 b 를 더하는 함수로 정의하는 것이다.

(define (f a b) (+ a b) )

이제 f 가 뜬다. 
만약 디버거 모드라면 변수창에  procedures => {{{f} {{a b} {+ a b}}}} 처럼 변하는 것을 알 수 있다. 의도했던 대로이며 대략 돌아간다는 의미다.
define 특히 톱레벨에서의 경우는 글로벌 환경에서 bind 로 환경을 변화시킨 것이다. 바인드가 하는 일은 오로지 환경의 변화이며 나머지는 없다. 

환경이 대략 구축된 것이다. 
다음번에는 eval을 만들 차례다.
상당히 귀찮은 작업이다. 왜냐하면 atom 과 같은 프로시저는 없기 때문이다.  그러면 일단 머릿속에서는 atom의 반대함수는 list? 로 이것을 not 해서 사용하는 수 밖에 없다는 생각을 한다. 

그러나 일단 돌아가면 또 빠르게 구현될 것은 분명하다.


Trackback 0 Comment 0