문법 술어, 환경 연산, 복합 프로시저 레코드, 인자 평가, 수열 평가, eval, apply가 하나의 유한 guest 프로그램에서 협력하는 조립된 평가기 핵심을 살펴봅니다.
생각해 볼 질문
어떤 책임이 eval에 있고 어떤 책임이 apply에 있으며 어떤 데이터 구조가 둘을 연결할까요?
자기 평가 값과 변수와 특수 형식과 적용 분류하기
대입·정의·if·lambda·begin·quote를 별도 규칙으로 보내기
적용적 순서로 연산자와 피연산자를 평가한 뒤 apply 호출하기
호스트 원시 프로시저와 표현된 복합 프로시저 적용하기
복합 적용마다 포획한 어휘 환경 확장하기
평가기 제어와 데이터로 표현한 guest 프로그램 분리하기
evaluate는 중심 문법 디스패처입니다. 직접 값은 그대로 반환하고 이름은 환경에서 조회하며 특수 형식은 각자의 평가 규칙으로 갑니다. 나머지 pair는 적용으로 다룹니다. 적용 절은 연산자와 피연산자 식을 평가한 뒤 얻은 프로시저와 인자 값을 apply-procedure에 넘깁니다.
apply-procedure는 소스 문법을 살피지 않습니다. 호스트 프로시저는 apply를 통해 인자 목록을 받고, compound-procedure 레코드는 매개변수와 본문 식과 생성 환경을 제공합니다. 적용은 그 환경에 새 프레임을 연결하고 본문 수열을 평가합니다. 재사용한 canonical driver는 하나의 지속 guest 환경에서 교육용 평가기의 핵심 형식을 모두 실행합니다.
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)))
예제
결과—
출력
—
값
—
진단
—
실행 추적0 / 0 개 이벤트
예상 결과
완전한 실행은 (complete ((defined square) (defined make-adder) (defined add-five) (defined base) 49 12 (assigned base) 81 6) 0)을 반환합니다. 5개 형식 예산 실행은 (truncated ((defined square) (defined make-adder) (defined add-five) (defined base) 49) 4)를 반환합니다.
실행 추적에서 볼 점
각 guest 형식에서 evaluate의 선택된 절을 먼저 찾으세요. 적용에서는 연산자 평가와 왼쪽부터 list-of-values 구성과 apply-procedure를 분리하세요. compound 적용마다 프로시저 레코드에 저장된 환경에 연결된 새 프레임을 따라가세요. driver는 최상위 형식마다 form budget을 한 단위 사용합니다.
직접 해보기
프로그램을 수정하고 결과를 비교해 보세요.
첫 형식에서 base를 바꾸고 마지막 형식에서 add-five를 호출하는 guest begin 식을 추가하세요. 실행 전에 새 식이 지날 모든 evaluate 절과 apply-procedure 갈래를 적으세요.
힌트 보기
begin은 eval-sequence로 갑니다. set!은 기존 프레임을 바꾸고 add-five는 make-adder가 만든 환경을 가진 compound procedure로 남습니다.