Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[108]

4.1 Метациклический интерпретатор

Наш интерпретатор Лиспа будет реализован как программа на Лиспе. Может показаться, что размышления о выполнении Лисп-программ при помощи интерпретатора, который сам написан на Лиспе, составляют порочный круг. Однако вычисление есть процесс, так что вполне логично описывать процесс вычисления с помощью Лиспа - в конце концов, это наш инструмент для описания процессов.3 Интерпретатор, написанный на языке, который он сам реализует, называется метациклическим (metacircular).

В сущности, метациклический интерпретатор является формулировкой на языке Scheme модели вычислений с окружениями, описанной в разделе 3.2. Напомним, что в этой модели было две основные части:

•Чтобы выполнить комбинацию (составное выражение, не являющееся особой формой), нужно вычислить его подвыражения и затем применить значение подвыражения-оператора к значениям подвыражений-операндов.

•Чтобы применить составную процедуру к набору аргументов, нужно выполнить тело процедуры в новом окружении. Для того, чтобы построить это окружение, нужно расширить окружение объекта-процедуры кадром, в котором формальные параметры процедуры связаны с аргументами, к которым процедура применяется.

Эти два правила описывают сущность процесса вычисления, основной цикл, в котором выражения, которые требуется выполнить в окружении, сводятся к процедурам, которые нужно применить к аргументам, а те, в свою очередь, сводятся к новым выражениям, которые нужно выполнить в новых окружениях, и так далее, пока мы не доберемся до символов, чьи значения достаточно найти в окружении, и элементарных процедур, которые применяются напрямую (см. рис. 4.1).4 Этот цикл вычисления будет построен в виде взаимодействия

3 Даже с учетом этого, остаются важные стороны процесса вычисления, которые в нашем интерпретаторе не проясняются. Самая важная из них - точные механизмы того, как одни процедуры вызывают другие и возвращают значения процедурам, которые их вызвали. Эти вопросы мы рассмотрим в главе 5, где мы исследуем процесс вычисления более внимательно, реализуя вычислитель как простую регистровую машину.

4Если нам дается возможность применять примитивы, то что остается сделать для реализации интерпретатора? Задача интерпретатора состоит не в том, чтобы определить примитивы языка, а в том, чтобы обеспечить связующие элементы - средства комбинирования и абстракции, - которые превращают набор примитивов в язык. А именно:

•Интерпретатор позволяет работать с вложенными выражениями. Например, чтобы вычислить значение выражения (+ 1 6), достаточно применения примитивов, но этого недостаточно для работы с выражением (+ 1 (* 2 3)). Сама по себе элементарная процедура + способна работать только с числами, и если передать ей аргумент - выражение (* 2 3), она сломается. Одна из важных задач интерпретатора - устроить вычисление так, чтобы (* 2 3) свелось к значению 6, прежде чем оно будет передано + как аргумент.

•Интерпретатор позволяет использовать переменные. Например, элементарная процедура сложения не знает, как работать с выражениями вроде (+ x 1). Нам нужен интерпретатор, чтобы следить за переменными и получать их значения, прежде чем запускать элементарные процедуры.

•Интерпретатор позволяет определять составные процедуры. При этом нужно хранить определения процедур, знать, как эти определения используются при вычислении выражений, и обеспечивать механизм, который позволяет процедурам принимать аргументы.

•Интерпретатор дает особые формы, вычисляющиеся иначе, чем вызовы процедур.


выражение, окружение

Рис. 4.1: Цикл eval-apply раскрывает сущность компьютерного языка.

двух основных процедур интерпретатора, eval и apply, описанных в разделе 4.1.1 (см. рис. 4.1).

Реализация интерпретатора будет зависеть от процедур, определяющих синтаксис (syntax) выполняемых выражений. При помощи абстракции данных мы сделаем интерпретатор независимым от представления языка. К примеру, вместо того, чтобы окончательно решать, что присваивание выражается в виде списка, в котором первым элементом стоит символ set!, мы пользуемся абстрактным предикатом assignment?, чтобы распознавать присваивание, и абстрактными селекторами assignment-variable и assignment-value, чтобы обращаться к его частям. Реализация выражений будет подробно рассмотрена в разделе 4.1.2. Имеются также операции, описанные в разделе 4.1.3, которые определяют представление процедур и окружений. Например, make-procedure порождает составные процедуры,lookup-variable-value извлекает значения переменных, а apply-primitive-procedure применяет элементарную процедуру к указанному списку аргументов.

4.1.1 Ядро интерпретатора

Процесс вычисления можно описать как взаимодействие двух процедур: eval и apply.

Eval

Процедура eval в качестве аргументов принимает выражение и окружение. Она относит выражение к одному из возможных классов и управляет его выполнением. Eval построена как разбор случаев в зависимости от синтаксического типа выполняемого выражения. Для того, чтобы процедура была достаточно общей, определение типа выражения мы формулируем абстрактно, не связывая себя никакой конкретной реализацией различных типов выражений. Для каждого типа выражений имеется предикат, который распознает этот тип, и абстрактные средства для выбора его частей. Такой абстрактный синтаксис (abstract syntax) позволяет легко видеть, как можно изменить синтаксис языка и использовать тот же самый интерпретатор, но только с другим набором синтаксических процедур.

процедура, аргументы


Элементарные выражения

•Для самовычисляющихся выражений, например, чисел, eval возвращает само выражение.

•Eval должен находить значения переменных, просматривая окружение. Особые формы

•Для выражений с кавычкой (quote), eval возвращает само закавыченное выражение.

•Присваивание переменной (или ее определение) должно вызвать eval рекурсивно, чтобы вычислить новое значение, которое требуется связать с переменной. Окружение нужно модифицировать, изменяя (или создавая) связывание для переменной.

•Выражение if требует специальной обработки своих частей: если предикат истинен, нужно выполнить следствие; если нет, альтернативу.

•Выражение lambda требуется преобразовать в процедуру, пригодную к применению. Для этого нужно упаковать параметры и тело lambda-выраже-ния вместе с окружением, в котором оно вычисляется.

•Выражение begin требует выполнения своих подвыражений в том порядке, как они появляются.

•Разбор случаев (cond) преобразуется во вложенные выражения if и затем вычисляется.

Комбинации

•Для применения процедуры eval должна рекурсивно вычислить операцию и операнды комбинации. Получившиеся процедура и аргументы передаются apply, которая распоряжается собственно применением процедуры.

Вот определение eval:

(define (eval exp env)

(cond ((self-evaluating? exp) exp)

((variable? exp) (lookup-variable-value exp env)) ((quoted? exp) (text-of-quotation exp)) ((assignment? exp) (eval-assignment exp env)) ((definition? exp) (eval-definition exp env)) ((if? exp) (eval-if exp env)) ((lambda? exp) (make-procedure (lambda-parameters exp) (lambda-body exp) env))

((begin? exp)

(eval-sequence (begin-actions exp) env)) ((cond? exp) (eval (cond->if exp) env)) ((application? exp)

(apply (eval (operator exp) env)

(list-of-values (operands exp) env)))

(else

(error "Неизвестный тип выражения -- EVAL" exp))))



[стр.Начало] [стр.1] [стр.2] [стр.3] [стр.4] [стр.5] [стр.6] [стр.7] [стр.8] [стр.9] [стр.10] [стр.11] [стр.12] [стр.13] [стр.14] [стр.15] [стр.16] [стр.17] [стр.18] [стр.19] [стр.20] [стр.21] [стр.22] [стр.23] [стр.24] [стр.25] [стр.26] [стр.27] [стр.28] [стр.29] [стр.30] [стр.31] [стр.32] [стр.33] [стр.34] [стр.35] [стр.36] [стр.37] [стр.38] [стр.39] [стр.40] [стр.41] [стр.42] [стр.43] [стр.44] [стр.45] [стр.46] [стр.47] [стр.48] [стр.49] [стр.50] [стр.51] [стр.52] [стр.53] [стр.54] [стр.55] [стр.56] [стр.57] [стр.58] [стр.59] [стр.60] [стр.61] [стр.62] [стр.63] [стр.64] [стр.65] [стр.66] [стр.67] [стр.68] [стр.69] [стр.70] [стр.71] [стр.72] [стр.73] [стр.74] [стр.75] [стр.76] [стр.77] [стр.78] [стр.79] [стр.80] [стр.81] [стр.82] [стр.83] [стр.84] [стр.85] [стр.86] [стр.87] [стр.88] [стр.89] [стр.90] [стр.91] [стр.92] [стр.93] [стр.94] [стр.95] [стр.96] [стр.97] [стр.98] [стр.99] [стр.100] [стр.101] [стр.102] [стр.103] [стр.104] [стр.105] [стр.106] [стр.107] [стр.108] [стр.109] [стр.110] [стр.111] [стр.112] [стр.113] [стр.114] [стр.115] [стр.116] [стр.117] [стр.118] [стр.119] [стр.120] [стр.121] [стр.122] [стр.123] [стр.124] [стр.125] [стр.126] [стр.127] [стр.128] [стр.129] [стр.130] [стр.131] [стр.132] [стр.133] [стр.134] [стр.135] [стр.136] [стр.137] [стр.138] [стр.139] [стр.140] [стр.141] [стр.142] [стр.143] [стр.144] [стр.145] [стр.146] [стр.147] [стр.148] [стр.149] [стр.150] [стр.151] [стр.152] [стр.153] [стр.154] [стр.155] [стр.156] [стр.157] [стр.158] [стр.159] [стр.160] [стр.161] [стр.162] [стр.163] [стр.164] [стр.165] [стр.166] [стр.167] [стр.168] [стр.169] [стр.170] [стр.171] [стр.172] [стр.173] [стр.174] [стр.175] [стр.176] [стр.177] [стр.178] [стр.179] [стр.180] [стр.181] [стр.182] [стр.183] [стр.184] [стр.185] [стр.186] [стр.187] [стр.188] [стр.189] [стр.190] [стр.191] [стр.192] [стр.193] [стр.194] [стр.195] [стр.196]