Синтаксические предикаты, операции с окружениями, записи составных процедур, вычисление аргументов и последовательностей связывают eval и apply.
Вопрос для размышления
Какие обязанности принадлежат eval, какие принадлежат apply и какие структуры данных связывают их между собой?
Классификация самовычисляющихся значений, переменных, специальных форм и применений
Направление присваивания, определения, if, lambda, begin и quote к отдельным правилам
Вычисление оператора и его операндов перед применением в аппликативном порядке
Применение примитива хоста или представленной составной процедуры
Расширение захваченного лексического окружения для каждого составного применения
Разделение управления вычислителем и программы guest, представленной в виде данных
Процедура evaluate является центральным диспетчером синтаксиса. Прямые значения возвращают сами себя, имена используют поиск в окружении, специальные формы получают собственные правила вычисления, а каждая оставшаяся пара рассматривается как применение. Ветка применения вычисляет выражения оператора и операндов, а затем передает полученную процедуру и значения аргументов в apply-procedure.
Процедура apply-procedure принимает уже классифицированные значения процедур. Процедура хоста получает список аргументов через apply. Запись compound-procedure предоставляет параметры, выражения тела и окружение своего создания, а применение расширяет это окружение новым кадром и вычисляет последовательность тела. Повторно используемый канонический драйвер выполняет каждую базовую форму в одном постоянном окружении guest и делает наглядным учебный цикл eval/apply.
Код SICP7,837 из 1,048,576 байт UTF-8
(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). Запуск с бюджетом в пять форм возвращает (truncated ((defined square) (defined make-adder) (defined add-five) (defined base) 49) 4).
На что обратить внимание в трассе
Для каждой формы guest начинайте с evaluate и определите выбранную ветку. При применениях разделяйте вычисление оператора, построение list-of-values слева направо и вызов apply-procedure. Проследите за каждой составной процедурой в новом кадре, связанном с окружением из ее записи процедуры. Бюджет форм верхнего уровня относится к драйверу и не входит в eval или apply.
Попробуйте сами
Измените программу и сравните результат.
Добавьте выражение guest begin, первая форма которого изменяет base, а последняя форма вызывает add-five. Перед запуском определите каждую ветку evaluate и одну ветку apply-procedure, которые использует новая форма.
Показать подсказку
Форма begin делегирует выполнение в eval-sequence. Форма set! изменяет существующий кадр, в то время как add-five остается составной процедурой, несущей окружение, созданное make-adder.