Определите пары, селекторы, бесконечные списки, map и take внутри guest-языка, чтобы отложенные аргументы составных процедур обеспечивали поведение потока.
Вопрос для размышления
Что происходит с абстракцией списков, когда вычислитель автоматически откладывает каждый операнд составной процедуры?
Определение lazy-cons как обычной составной процедуры в языке guest
Выбор головы без форсирования отложенного параметра хвоста
Создание самоссылающегося бесконечного связывания ones
Рекурсивное порождение неограниченного списка целых чисел
Отображение процедуры guest на ленивый список без построения его полного хвоста
Использование строгого конечного потребителя для запроса точного конечного списка результатов
lazy-cons записывается как процедура, возвращающая процедуру-селектор. В ленивом вычислителе ее параметры x и y являются thunk. lazy-car применяет пару к селектору, возвращающему x, поэтому y остается нефорсированным. Глобальное определение ones поэтому может ссылаться на ones в собственном отложенном хвосте: это обращение не запрашивается, пока lazy-cdr не дойдет до него после создания связывания.
integers-from и lazy-map используют тот же обычный рекурсивный синтаксис. Их рекурсивные вызовы передаются как отложенные операнды y в lazy-cons, поэтому бесконечный остаток представлен мемоизированными thunk вычислителя. take служит границей требования: примитивному cons нужны фактические значения аргументов, поэтому он форсирует ровно запрошенное число голов и хвостов в конечный базовый список. Этот пример объединяет ленивые списки с вычислителем урока и делает требование потребителя явным.
Код SICP7,774 из 1,048,576 байт UTF-8
(begin(define(tagged-list?expressiontag)(and(pair?expression)(eq?(carexpression)tag)))(define(self-evaluating?expression)(or(number?expression)(string?expression)(boolean?expression)))(define(quoted?expression)(tagged-list?expression'quote))(define(if?expression)(tagged-list?expression'if))(define(lambda?expression)(tagged-list?expression'lambda))(define(begin?expression)(tagged-list?expression'begin))(define(definition?expression)(tagged-list?expression'define))(define(text-of-quotationexpression)(cadrexpression))(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(definition-variableexpression)(if(symbol?(cadrexpression))(cadrexpression)(car(cadrexpression))))(define(definition-valueexpression)(if(symbol?(cadrexpression))(caddrexpression)(cons'lambda(cons(cdr(cadrexpression))(cddrexpression)))))(define(operatorexpression)(carexpression))(define(operandsexpression)(cdrexpression))(define(pair-bindingsvariablesvalues)(cond((and(null?variables)(null?values))'())((null?variables)(error"too many guest arguments"))((null?values)(error"too few guest 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 lazy guest variable"variable)(let((record(assocvariable(frame-bindings(first-frameenvironment)))))(ifrecord(cdrrecord)(lookup-variable-valuevariable(cdrenvironment))))))(define(define-variable!variablevalueenvironment)(let*((frame(first-frameenvironment))(record(assocvariable(frame-bindingsframe))))(ifrecord(set-cdr!recordvalue)(set-cdr!frame(cons(consvariablevalue)(frame-bindingsframe)))))(list'definedvariable))(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(delay-itexpressionenvironment)(list'thunkexpressionenvironment))(define(thunk?object)(tagged-list?object'thunk))(define(evaluated-thunk?object)(tagged-list?object'evaluated-thunk))(define(force-itobject)(cond((thunk?object)(let((result(actual-value(cadrobject)(caddrobject))))(set-car!object'evaluated-thunk)(set-car!(cdrobject)result)(set-cdr!(cdrobject)'())result))((evaluated-thunk?object)(cadrobject))(elseobject)))(define(actual-valueexpressionenvironment)(force-it(lazy-evalexpressionenvironment)))(define(true?value)(not(eq?value#f)))(define(eval-ifexpressionenvironment)(if(true?(actual-value(if-predicateexpression)environment))(lazy-eval(if-consequentexpression)environment)(lazy-eval(if-alternativeexpression)environment)))(define(eval-sequenceexpressionsenvironment)(cond((null?expressions)'ok)((null?(cdrexpressions))(lazy-eval(carexpressions)environment))(else(actual-value(carexpressions)environment)(eval-sequence(cdrexpressions)environment))))(define(eval-definitionexpressionenvironment)(define-variable!(definition-variableexpression)(actual-value(definition-valueexpression)environment)environment))(define(list-of-arg-valuesexpressionsenvironment)(if(null?expressions)'()(cons(actual-value(carexpressions)environment)(list-of-arg-values(cdrexpressions)environment))))(define(list-of-delayed-argsexpressionsenvironment)(if(null?expressions)'()(cons(delay-it(carexpressions)environment)(list-of-delayed-args(cdrexpressions)environment))))(define(apply-lazyprocedureargument-expressionscalling-environment)(cond((procedure?procedure)(applyprocedure(list-of-arg-valuesargument-expressionscalling-environment)))((compound-procedure?procedure)(eval-sequence(procedure-bodyprocedure)(extend-environment(procedure-parametersprocedure)(list-of-delayed-argsargument-expressionscalling-environment)(procedure-environmentprocedure))))(else(error"not a lazy guest procedure"procedure))))(define(lazy-evalexpressionenvironment)(cond((self-evaluating?expression)expression)((symbol?expression)(lookup-variable-valueexpressionenvironment))((quoted?expression)(text-of-quotationexpression))((if?expression)(eval-ifexpressionenvironment))((lambda?expression)(make-procedure(lambda-parametersexpression)(lambda-bodyexpression)environment))((begin?expression)(eval-sequence(begin-actionsexpression)environment))((definition?expression)(eval-definitionexpressionenvironment))((pair?expression)(apply-lazy(actual-value(operatorexpression)environment)(operandsexpression)environment))(else(error"unknown lazy guest expression"expression))))(defineprobes0)(define(probevalue)(set!probes(+probes1))value)(defineprimitive-bindings(list(cons'++)(cons'--)(cons'**)(cons'//)(cons'==)(cons'<<)(cons'>>)(cons'listlist)(cons'conscons)(cons'carcar)(cons'cdrcdr)(cons'null?null?)(cons'pair?pair?)(cons'probeprobe)))(defineglobal-environment(list(cons'*frame*primitive-bindings)))(defineguest-program'(begin(define(lazy-consxy)(lambda(selector)(selectorxy)))(define(lazy-carpair)(pair(lambda(xy)x)))(define(lazy-cdrpair)(pair(lambda(xy)y)))(defineones(lazy-cons1ones))(define(takestreamcount)(if(=count0)(quote())(cons(lazy-carstream)(take(lazy-cdrstream)(-count1)))))(takeones5)))(actual-valueguest-programglobal-environment))
Примеры
Результат—
Вывод
—
Значение
—
Диагностика
—
Трасса выполнения0 / 0 событий
Ожидаемый результат
Самоссылающийся список возвращает (1 1 1 1 1). Список отображенных целых чисел возвращает (1 4 9 16 25 36).
На что обратить внимание в трассе
Найдите каждое применение lazy-cons и проверьте отложенный операнд y. При запуске ones убедитесь, что самоссылка впервые форсируется только через lazy-cdr после того, как define установил связывание. При запуске с отображением подсчитайте, сколько применений integers-from и square запрашивает take, вместо представления завершенного бесконечного списка.
Попробуйте сами
Измените программу и сравните результат.
Определите lazy-filter и извлеките первые пять четных целых чисел. Затем запросите тот же префикс дважды и проверьте, какие thunk вычислителя используются повторно после мемоизации.
Показать подсказку
Если значение не проходит предикат, продолжайте с отложенным хвостом. Если проходит, создайте один lazy-cons, чей рекурсивный вызов filter останется отложенным вторым аргументом.