명시적인 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를 유지합니다. 두 번째 실행은 다섯 형식 뒤에 멈추고 형식 네 개가 남았다고 보고합니다. 이 형식 예산은 한 형식 안의 재귀나 작업량을 제한하지 않으며 그 아래 실행 경계는 패키지된 브라우저 런타임이 담당합니다.
SICP 코드UTF-8 7,837 / 1,048,576바이트
(begin(define(tagged-list?expressiontag)(if(pair?expression)(eq?(carexpression)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-quotationexpression)(cadrexpression))(define(assignment-variableexpression)(cadrexpression))(define(assignment-valueexpression)(caddrexpression))(define(definition-variableexpression)(if(symbol?(cadrexpression))(cadrexpression)(car(cadrexpression))))(define(definition-valueexpression)(if(symbol?(cadrexpression))(caddrexpression)(cons'lambda(cons(cdr(cadrexpression))(cddrexpression)))))(define(if-predicateexpression)(cadrexpression))(define(if-consequentexpression)(caddrexpression))(define(if-alternativeexpression)(cadddrexpression))(define(lambda-parametersexpression)(cadrexpression))(define(lambda-bodyexpression)(cddrexpression))(define(begin-actionsexpression)(cdrexpression))(define(operatorexpression)(carexpression))(define(operandsexpression)(cdrexpression))(define(pair-bindingsvariablesvalues)(cond((and(null?variables)(null?values))'())((null?variables)(error"too many arguments"))((null?values)(error"too few arguments"))(else(cons(cons(carvariables)(carvalues))(pair-bindings(cdrvariables)(cdrvalues))))))(define(make-framevariablesvalues)(cons'*frame*(pair-bindingsvariablesvalues)))(define(first-frameenvironment)(carenvironment))(define(frame-bindingsframe)(cdrframe))(define(extend-environmentvariablesvaluesbase)(cons(make-framevariablesvalues)base))(define(lookup-variable-valuevariableenvironment)(if(null?environment)(error"unbound variable"variable)(let((binding(assocvariable(frame-bindings(first-frameenvironment)))))(ifbinding(cdrbinding)(lookup-variable-valuevariable(cdrenvironment))))))(define(define-variable!variablevalueenvironment)(let*((frame(first-frameenvironment))(binding(assocvariable(frame-bindingsframe))))(ifbinding(set-cdr!bindingvalue)(set-cdr!frame(cons(consvariablevalue)(frame-bindingsframe)))))(list'definedvariable))(define(set-variable-value!variablevalueenvironment)(if(null?environment)(error"unbound assignment"variable)(let((binding(assocvariable(frame-bindings(first-frameenvironment)))))(ifbinding(begin(set-cdr!bindingvalue)(list'assignedvariable))(set-variable-value!variablevalue(cdrenvironment))))))(define(make-procedureparametersbodyenvironment)(list'compoundparametersbodyenvironment))(define(compound-procedure?procedure)(tagged-list?procedure'compound))(define(procedure-parametersprocedure)(cadrprocedure))(define(procedure-bodyprocedure)(caddrprocedure))(define(procedure-environmentprocedure)(cadddrprocedure))(define(list-of-valuesexpressionsenvironment)(if(null?expressions)'()(cons(evaluate(carexpressions)environment)(list-of-values(cdrexpressions)environment))))(define(eval-sequenceexpressionsenvironment)(if(null?(cdrexpressions))(evaluate(carexpressions)environment)(begin(evaluate(carexpressions)environment)(eval-sequence(cdrexpressions)environment))))(define(eval-ifexpressionenvironment)(if(evaluate(if-predicateexpression)environment)(evaluate(if-consequentexpression)environment)(evaluate(if-alternativeexpression)environment)))(define(eval-assignmentexpressionenvironment)(set-variable-value!(assignment-variableexpression)(evaluate(assignment-valueexpression)environment)environment))(define(eval-definitionexpressionenvironment)(define-variable!(definition-variableexpression)(evaluate(definition-valueexpression)environment)environment))(define(apply-procedureprocedurearguments)(cond((procedure?procedure)(applyprocedurearguments))((compound-procedure?procedure)(eval-sequence(procedure-bodyprocedure)(extend-environment(procedure-parametersprocedure)arguments(procedure-environmentprocedure))))(else(error"not a guest procedure"procedure))))(define(evaluateexpressionenvironment)(cond((self-evaluating?expression)expression)((symbol?expression)(lookup-variable-valueexpressionenvironment))((quoted?expression)(text-of-quotationexpression))((assignment?expression)(eval-assignmentexpressionenvironment))((definition?expression)(eval-definitionexpressionenvironment))((if?expression)(eval-ifexpressionenvironment))((lambda?expression)(make-procedure(lambda-parametersexpression)(lambda-bodyexpression)environment))((begin?expression)(eval-sequence(begin-actionsexpression)environment))((pair?expression)(apply-procedure(evaluate(operatorexpression)environment)(list-of-values(operandsexpression)environment)))(else(error"unknown guest expression"expression))))(defineprimitive-bindings(list(cons'++)(cons'--)(cons'**)(cons'//)(cons'==)(cons'<<)(cons'>>)(cons'conscons)(cons'carcar)(cons'cdrcdr)(cons'listlist)(cons'null?null?)(cons'pair?pair?)(cons'notnot)))(define(make-global-environment)(list(cons'*frame*primitive-bindings)))(define(run-programformsenvironmentform-budget)(define(loopremainingbudgettranscript)(cond((null?remaining)(list'complete(reversetranscript)0))((=budget0)(list'truncated(reversetranscript)(lengthremaining)))(else(loop(cdrremaining)(-budget1)(cons(evaluate(carremaining)environment)transcript)))))(loopformsform-budget'()))(defineguest-program'((define(squarex)(*xx))(define(make-adderx)(lambda(y)(+xy)))(defineadd-five(make-adder5))(definebase5)(square(+base2))(add-five7)(set!base9)(if(>base8)(squarebase)0)(add-five1)))(list(run-programguest-program(make-global-environment)20)(run-programguest-program(make-global-environment)5)))
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입니다. 제한 실행에서는 기존 정의 뒤에 형식을 어디에 끼워 넣는지에 따라 다섯 번째로 끝나는 식이 달라집니다.