Система eval/apply вычисляет цитированные формы верхнего уровня в одном глобальном окружении и возвращает протокол определений и значений.
Вопрос для размышления
Что превращает набор процедур вычислителя в программу, способную выполнять последовательность пользовательских форм?
Разделение выполнения хоста Lispex и выражений guest, представленных в виде данных
Диспетчеризация самовычисляющихся значений, переменных, специальных форм и применений
Применение примитивных процедур хоста и представленных составных процедур
Сохранение определений и присваиваний в одном явном глобальном окружении
Наблюдение захвата лексического замыкания при последующих глобальных изменениях
Формирование отчета о полном или усеченном конечном запуске драйвера верхнего уровня
Разделение бюджета форм верхнего уровня и объема работы внутри одной формы
Вычислитель представляет окружения как списки изменяемых кадров. Поиск переменной guest просматривает эти кадры, define изменяет первый кадр, а set! ищет существующее связывание и изменяет эту запись. Форма guest lambda становится записью составной процедуры, содержащей параметры, выражения тела и окружение, в котором вычислялась эта lambda. Процедура apply-procedure либо применяет примитив хоста, либо вычисляет составное тело в новом кадре.
Процедура run-program служит драйвером. Она принимает цитированные формы верхнего уровня, одно явное глобальное окружение и бюджет форм. Каждая завершенная форма добавляет одно значение в transcript, при этом определения и присваивания остаются видимыми для последующих форм. Полный запуск определяет square, make-adder, add-five и base, а add-five сохраняет локальное значение x равным 5 даже после изменения глобальной переменной base. Второй запуск останавливается после пяти форм и сообщает, что четыре формы еще ожидают обработки. Этот бюджет форм не ограничивает рекурсию или работу внутри одной формы, а ограничения выполнения более низкого уровня по-прежнему обеспечивает упакованная среда выполнения браузера.
Код 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).
На что обратить внимание в трассе
Отделяйте вызовы хоста, реализующие evaluate и apply-procedure, от выражений guest, сохраненных в guest-program. Проследите за изменением глобального кадра при выполнении define и set!, за новым кадром, создаваемым для каждого составного применения, и за окружением, захваченным add-five. В run-program одна единица бюджета расходуется только после возврата формы верхнего уровня, и этот счетчик не измеряет рекурсивную работу guest внутри этой формы.
Попробуйте сами
Измените программу и сравните результат.
Добавьте определение guest (define (twice procedure value) (procedure (procedure value))) и вычислите (twice add-five 1). Спрогнозируйте новое значение transcript и количество оставшихся форм при сохранении бюджета форм равным 5.
Показать подсказку
Полный запуск применяет add-five дважды, поэтому результат guest равен 11. В запуске с бюджетом в пять форм вставка еще одной формы после существующих определений меняет то, какое выражение станет пятой завершенной формой.