평가기는 유한한 프로그램을 읽고 환경을 보존할 수 있다.
작은 eval/apply 체계는 인용된 최상위 형식을 하나의 가변 전역 환경에서 평가하고 정의와 값의 transcript를 반환하며 명시적인 형식 예산이 배치를 끝냈는지 보고합니다.
평가기 프로시저 모음을 사용자 형식의 수열을 실행하는 하나의 프로그램으로 만드는 것은 무엇일까요?
- 호스트 리스펙스 실행과 데이터로 표현한 guest 식 구분하기
- 자기 평가 값과 변수와 특수 형식과 적용 디스패치하기
- 호스트 원시 프로시저와 표현된 복합 프로시저 적용하기
- 하나의 명시적인 전역 환경에 정의와 대입 보존하기
- 나중의 전역 변경 뒤에도 어휘 클로저가 캡처한 값 관찰하기
- 유한한 최상위 드라이버 실행을 complete 또는 truncated로 보고하기
- 최상위 형식 예산과 한 형식 안의 작업량 구분하기
평가기는 환경을 가변 프레임의 리스트로 표현합니다. guest 변수 조회는 그 프레임을 검색하고 define은 첫 프레임을 바꾸며 set!은 기존 바인딩을 찾아 그 레코드를 바꿉니다. guest lambda는 매개변수와 본문 식과 lambda가 평가된 환경을 담은 복합 프로시저 레코드가 됩니다. apply-procedure는 호스트 원시 프로시저를 적용하거나 새 프레임에서 복합 본문을 평가합니다.
run-program이 드라이버입니다. 인용된 최상위 형식과 하나의 전역 환경과 형식 예산을 받습니다. 끝난 형식마다 transcript 값 하나를 남기며 정의와 대입은 뒤 형식에서도 보입니다. 전체 실행은 square와 make-adder와 add-five와 base를 정의합니다. add-five는 전역 base가 바뀐 뒤에도 지역 x 값 5를 유지합니다. 두 번째 실행은 다섯 형식 뒤에 멈추고 형식 네 개가 남았다고 보고합니다. 이 형식 예산은 한 형식 안의 재귀나 작업량을 제한하지 않으며 그 아래 실행 경계는 패키지된 브라우저 런타임이 담당합니다.
(begin
(define (tagged-list? expression tag)
(if (pair? expression)
(eq? (car expression) tag)
#f))
(define (self-evaluating? expression)
(cond ((number? expression) #t)
((string? expression) #t)
((boolean? expression) #t)
(else #f)))
(define (quoted? expression)
(tagged-list? expression 'quote))
(define (assignment? expression)
(tagged-list? expression 'set!))
(define (definition? expression)
(tagged-list? expression 'define))
(define (if? expression)
(tagged-list? expression 'if))
(define (lambda? expression)
(tagged-list? expression 'lambda))
(define (begin? expression)
(tagged-list? expression 'begin))
(define (text-of-quotation expression) (cadr expression))
(define (assignment-variable expression) (cadr expression))
(define (assignment-value expression) (caddr expression))
(define (definition-variable expression)
(if (symbol? (cadr expression))
(cadr expression)
(car (cadr expression))))
(define (definition-value expression)
(if (symbol? (cadr expression))
(caddr expression)
(cons 'lambda
(cons (cdr (cadr expression))
(cddr expression)))))
(define (if-predicate expression) (cadr expression))
(define (if-consequent expression) (caddr expression))
(define (if-alternative expression) (cadddr expression))
(define (lambda-parameters expression) (cadr expression))
(define (lambda-body expression) (cddr expression))
(define (begin-actions expression) (cdr expression))
(define (operator expression) (car expression))
(define (operands expression) (cdr expression))
(define (pair-bindings variables values)
(cond ((and (null? variables) (null? values)) '())
((null? variables) (error "too many arguments"))
((null? values) (error "too few arguments"))
(else
(cons (cons (car variables) (car values))
(pair-bindings (cdr variables)
(cdr values))))))
(define (make-frame variables values)
(cons '*frame* (pair-bindings variables values)))
(define (first-frame environment) (car environment))
(define (frame-bindings frame) (cdr frame))
(define (extend-environment variables values base)
(cons (make-frame variables values) base))
(define (lookup-variable-value variable environment)
(if (null? environment)
(error "unbound variable" variable)
(let ((binding
(assoc variable
(frame-bindings (first-frame environment)))))
(if binding
(cdr binding)
(lookup-variable-value variable (cdr environment))))))
(define (define-variable! variable value environment)
(let* ((frame (first-frame environment))
(binding (assoc variable (frame-bindings frame))))
(if binding
(set-cdr! binding value)
(set-cdr! frame
(cons (cons variable value)
(frame-bindings frame)))))
(list 'defined variable))
(define (set-variable-value! variable value environment)
(if (null? environment)
(error "unbound assignment" variable)
(let ((binding
(assoc variable
(frame-bindings (first-frame environment)))))
(if binding
(begin
(set-cdr! binding value)
(list 'assigned variable))
(set-variable-value! variable value (cdr environment))))))
(define (make-procedure parameters body environment)
(list 'compound parameters body environment))
(define (compound-procedure? procedure)
(tagged-list? procedure 'compound))
(define (procedure-parameters procedure) (cadr procedure))
(define (procedure-body procedure) (caddr procedure))
(define (procedure-environment procedure) (cadddr procedure))
(define (list-of-values expressions environment)
(if (null? expressions)
'()
(cons (evaluate (car expressions) environment)
(list-of-values (cdr expressions) environment))))
(define (eval-sequence expressions environment)
(if (null? (cdr expressions))
(evaluate (car expressions) environment)
(begin
(evaluate (car expressions) environment)
(eval-sequence (cdr expressions) environment))))
(define (eval-if expression environment)
(if (evaluate (if-predicate expression) environment)
(evaluate (if-consequent expression) environment)
(evaluate (if-alternative expression) environment)))
(define (eval-assignment expression environment)
(set-variable-value!
(assignment-variable expression)
(evaluate (assignment-value expression) environment)
environment))
(define (eval-definition expression environment)
(define-variable!
(definition-variable expression)
(evaluate (definition-value expression) environment)
environment))
(define (apply-procedure procedure arguments)
(cond ((procedure? procedure)
(apply procedure arguments))
((compound-procedure? procedure)
(eval-sequence
(procedure-body procedure)
(extend-environment
(procedure-parameters procedure)
arguments
(procedure-environment procedure))))
(else
(error "not a guest procedure" procedure))))
(define (evaluate expression environment)
(cond ((self-evaluating? expression) expression)
((symbol? expression)
(lookup-variable-value expression environment))
((quoted? expression)
(text-of-quotation expression))
((assignment? expression)
(eval-assignment expression environment))
((definition? expression)
(eval-definition expression environment))
((if? expression)
(eval-if expression environment))
((lambda? expression)
(make-procedure
(lambda-parameters expression)
(lambda-body expression)
environment))
((begin? expression)
(eval-sequence (begin-actions expression) environment))
((pair? expression)
(apply-procedure
(evaluate (operator expression) environment)
(list-of-values (operands expression) environment)))
(else
(error "unknown guest expression" expression))))
(define primitive-bindings
(list (cons '+ +) (cons '- -) (cons '* *) (cons '/ /)
(cons '= =) (cons '< <) (cons '> >)
(cons 'cons cons) (cons 'car car) (cons 'cdr cdr)
(cons 'list list) (cons 'null? null?)
(cons 'pair? pair?) (cons 'not not)))
(define (make-global-environment)
(list (cons '*frame* primitive-bindings)))
(define (run-program forms environment form-budget)
(define (loop remaining budget transcript)
(cond ((null? remaining)
(list 'complete (reverse transcript) 0))
((= budget 0)
(list 'truncated
(reverse transcript)
(length remaining)))
(else
(loop (cdr remaining)
(- budget 1)
(cons (evaluate (car remaining) environment)
transcript)))))
(loop forms form-budget '()))
(define guest-program
'((define (square x) (* x x))
(define (make-adder x) (lambda (y) (+ x y)))
(define add-five (make-adder 5))
(define base 5)
(square (+ base 2))
(add-five 7)
(set! base 9)
(if (> base 8) (square base) 0)
(add-five 1)))
(list
(run-program guest-program (make-global-environment) 20)
(run-program guest-program (make-global-environment) 5)))- 출력
- —
- 값
- —
- 진단
- —
전체 실행은 (complete ((defined square) (defined make-adder) (defined add-five) (defined base) 49 12 (assigned base) 81 6) 0)을 반환합니다. 제한한 실행은 (truncated ((defined square) (defined make-adder) (defined add-five) (defined base) 49) 4)를 반환합니다.
evaluate와 apply-procedure를 구현하는 호스트 호출과 guest-program에 저장된 guest 식을 구분하세요. define과 set!에 따른 전역 프레임 변경, 복합 적용마다 만든 새 프레임, add-five가 캡처한 환경을 따라가세요. run-program의 예산은 최상위 형식 하나가 반환된 뒤에만 한 단위 줄며 그 형식 내부의 재귀 작업을 재는 계수기가 아닙니다.
힌트를 보기 전에 프로그램을 바꿔 보세요.
guest에 (define (twice procedure value) (procedure (procedure value)))를 추가하고 (twice add-five 1)을 평가하세요. 새 transcript 값과 형식 예산을 5로 유지했을 때 남는 형식 수를 예상하세요.
힌트 하나 보기
전체 실행은 add-five를 두 번 적용하므로 guest 결과가 11입니다. 제한 실행에서는 기존 정의 뒤에 형식을 어디에 끼워 넣는지에 따라 다섯 번째로 끝나는 식이 달라집니다.