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


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




[164]

Мы использовали тот факт, что сохранять информацию необязательно. Если бы мы до этого не додумались, можно было бы реализовать eval-sequence так, что все выражения в последовательности обрабатываются одинаково - сохранение регистров, вычисление выражения, возврат с сохранением регистров,

27

и повторение до тех пор, пока не вычислятся все выражения:27

ev-sequence

(test (op no-more-exps?) (reg unev)) (branch (label ev-sequence-end)) (assign exp (op first-exp) (reg unev)) (save unev) (save env)

(assign continue (label ev-sequence-continue)) (goto (label eval-dispatch)) ev-sequence-continue (restore env) (restore unev)

(assign unev (op rest-exps) (reg unev)) (goto (label ev-sequence)) ev-sequence-end

(restore continue) (goto (reg continue))

Может показаться, что это мелкое изменение в предыдущем коде для выполнения последовательности: единственная разница состоит в том, что мы проходим через цикл сохранения-восстановления для последнего выражения последовательности так же, как и для остальных. Интерпретатор по-прежнему будет возвращать для всех выражений то же самое значение. Однако такое изменение теряет свойство хвостовой рекурсии, поскольку теперь после вычисления последнего выражения в последовательности нам придется возвращаться и отменять (бесполезные) сохранения регистров. Эти дополнительные сохранения будут накапливаться в гнезде рекурсивных вызовов. Следовательно, процессы вроде sqrt-iter потребуют памяти пропорционально количеству итераций, а не фиксированного объема. Такая разница может быть существенна. Например, при наличии хвостовой рекурсии можно выразить бесконечный цикл с помощью одного только механизма вызова процедур:

(define (count n) (newline) (display n) (count (+ n 1)))

Без хвостовой рекурсии такая процедура рано или поздно исчерпает место в стеке, а итерацию придется выражать с помощью какого-то другого механизма помимо вызовов процедур.

5.4.3 Условные выражения, присваивания и определения

Как и в метациклическом интерпретаторе, особые формы обрабатываются путем частичного выполнения частей выражения. В выражении if нам нужно

27Можно определить no-more-exps? как (define (no-more-exps? seq) (null? seq))


вычислить предикат, а затем на основании его значения решить, требуется нам выполнять следствие или альтернативу.

Прежде чем вычислять предикат, мы сохраняем само выражение if, поскольку позже из него потребуется извлекать следствие либо альтернативу. Кроме того, мы сохраняем окружение, которое потребуется при вычислении следствия или альтернативы, и continue, который потребуется нам при возврате значения выражению, ждущему результата if.

ev-if

(save exp); сохраняем выражение

(save env) (save continue)

(assign continue (label ev-if-decide))

(assign exp (op if-predicate) (reg exp))

(goto (label eval-dispatch)) ; вычисляем предикат

Вернувшись после вычисления предиката, мы смотрим, является ли его значение истиной или ложью, в зависимости от этого переносим в регистр exp следствие либо альтернативу, и идем на eval-dispatch. Заметим, что после восстановления env и continue eval-dispatch будет выполняться в правильном окружении и вернется после вычисления выражения в правильное место.

ev-if-decide

(restore continue) (restore env) (restore exp)

(test (op true?) (reg val))

(branch (label ev-if-consequent)) ev-if-alternative

(assign exp (op if-alternative) (reg exp))

(goto (label eval-dispatch)) ev-if-consequent

(assign exp (op if-consequent) (reg exp))

(goto (label eval-dispatch))

Присваивания и определения

Присваивания обрабатываются по метке ev-assignment, на которую переход происходит из eval-dispatch с выражением-присваиванием в регистре exp. Код ev-assignment сначала вычисляет значение присваиваемого выражения, а затем заносит это значение в окружение. Предполагается, что set-variable-value! дана как машинная операция.

ev-assignment

(assign unev (op assignment-variable) (reg exp)) (save unev); сохранить переменную

(assign exp (op assignment-value) (reg exp)) (save env) (save continue)

(assign continue (label ev-assignment-1))

(goto (label eval-dispatch)) ; вычислить присваиваемое значение ev-assignment-1


(restore continue) (restore env) (restore unev) (perform

(op set-variable-value!) (reg unev) (reg val) (reg env)) (assign val (const ok)) (goto (reg continue))

Подобным образом обрабатываются и определения:

ev-definition

(assign unev (op definition-variable) (reg exp)) (save unev); сохранить переменную

(assign exp (op definition-value) (reg exp)) (save env) (save continue)

(assign continue (label ev-definition-1))

(goto (label eval-dispatch)) ; вычислить значение переменной ev-definition-1

(restore continue) (restore env) (restore unev) (perform

(op define-variable!) (reg unev) (reg val) (reg env)) (assign val (const ok)) (goto (reg continue))

Упражнение 5.23.

Расширьте вычислитель так, чтобы обрабатывались производные выражения cond, let и тому подобные (раздел 4.1.2). Можно «сжульничать» и считать, что синтаксические трансформации вроде cond->if имеются как машинные операции.28

Упражнение 5.24.

Реализуйте cond как новую особую форму, не сводя его к if. Придется организовать цикл, проверяющий предикаты последовательных ветвей cond, пока один не окажется истинным, а затем с помощью ev-sequence выполнять действия этой ветви.

Упражнение 5.25.

Измените вычислитель так, чтобы он использовал нормальный порядок вычислений, на основе ленивого интерпретатора из раздела 4.2.

5.4.4 Запуск вычислителя

Реализовав вычислитель с явным управлением, мы подходим к концу сюжета, начатого в главе 1 - построения все более точных моделей для процесса вычисления. Мы начали с относительно неформальной подстановочной модели, затем в главе 3 расширили ее до модели с окружениями, позволившей работать с состоянием и его изменением. В метациклическом интерпретаторе из

28На самом деле это не жульничество. В настоящей реализации, построенной с нуля, мы бы на синтаксическом этапе, происходящем раньше собственно выполнения, интерпретировали при помощи вычислителя с явным управлением Scheme-программу, которая производит трансформации исходного кода вроде cond->if.



[стр.Начало] [стр.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]