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


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




[144]

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

Когда мы реализовывали в разделе 4.1 интерпретатор, мы видели, как можно избежать конфликтов между именами параметров процедур при помощи локальных окружений. Например, при вычислении

(define (square x) (* x x))

(define (sum-of-squares x y) (+ (square x) (square y)))

(sum-of-squares 3 4)

не возникает смешения между x из square и x из sum-of-squares, поскольку тело каждой процедуры мы вычисляем в окружении, которое специально построено для связывания локальных переменных. В запросной системе мы избегаем конфликтов имен при применении правил с помощью другой стратегии. Каждый раз при применении правила мы переименовываем переменные и даем им новые имена, которые обязаны быть уникальными. Аналогичная стратегия в интерпретаторе Лиспа заключалась бы в том, чтобы отменить внутренние окружения и просто переименовывать переменные в теле процедуры каждый раз, как мы ее вызываем.

Реализуйте для языка запросов метод применения правил, который использует не переименования, а окружения. Рассмотрите, можно ли использовать Вашу систему окружений для построения в языке запросов конструкций для работы с большими системами, например аналога блочной структуры процедур для правил. Можно ли связать это с проблемой ведения рассуждений в контексте (например: «Если бы я предположил, что истинно P, то я смог бы доказать A и В») в качестве метода решения задач? (Это упражнение не имеет однозначного решения. Хороший ответ, скорее всего, мог бы служить темой диссертации.)


Глава 5

Вычисления на регистровых машинах

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

Иоганн Кеплер (письмо к Герварту фон Гогенбургу, 1605)

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

В этой главе мы будем описывать процессы в терминах пошаговых операций традиционного компьютера. Такой компьютер, или регистровая машина (register machine), последовательно выполняет команды (instructions), которые работают с ограниченным числом элементов памяти, называемых регистрами (registers). Типичная команда регистровой машины применяет элементарную операцию к содержимому нескольких регистров и записывает результат еще в один регистр. Наши описания процессов, выполняемых регистровыми машинами, будут очень похожи на «машинный язык» обыкновенных компьютеров. Однако вместо того, чтобы сосредоточиться на машинном языке какого-то конкретного компьютера, мы рассмотрим несколько процедур на Лиспе и спроектируем специальную регистровую машину для выполнения каждой из этих


процедур. Таким образом, мы будем решать задачу с точки зрения архитектора аппаратуры, а не с точки зрения программиста на машинном языке компьютера. При проектировании регистровых машин мы разработаем механизмы для реализации важных программистских конструкций, таких, как рекурсия. Кроме того, мы представим язык для описания регистровых машин. В разделе 5.2 мы реализуем программу на Лиспе, которая с помощью этих описаний имитирует проектируемые нами машины.

Большинство элементарных операций наших регистровых машин очень просты. Например, такая операция может складывать числа, взятые из двух регистров, и сохранять результат в третьем. Несложно описать устройство, способное выполнять такие операции. Однако для работы со списковыми структурами мы будем использовать также операции car, cdr и cons, а они требуют сложного механизма выделения памяти. В разделе 5.3 мы изучаем их реализацию на основе более простых операций.

В разделе 5.4, накопив опыт выражения простых процессов в виде регистровых машин, мы спроектируем машину, которая реализует алгоритм, описываемый метациклическим интерпретатором из раздела 4.1. Таким образом, окажется заполненным пробел в нашем понимании того, как интерпретируются выражения языка Scheme, поскольку будет представлена явная модель механизмов управления вычислителя. В разделе 5.5 мы рассмотрим простой компилятор, переводящий программы на Scheme в последовательности команд, которые можно впрямую выполнить с помощью регистров и операций регистровой машины-вычислителя.

5.1 Проектирование регистровых машин

Чтобы спроектировать регистровую машину, требуется описать ее пути данных (data paths), то есть регистры и операции, а также контроллер (controller), который управляет последовательностью этих операций. Чтобы продемонстрировать строение простой регистровой машины, рассмотрим алгоритм Евклида для вычисления наибольшего общего делителя (НОД) двух натуральных чисел. Как мы видели в разделе 1.2.5, алгоритм Евклида можно реализовать в виде итеративного процесса, который описывается следующей процедурой:

(define (gcd a b)

(if (= b 0)

a

(gcd b (remainder a b))))

Машина, реализующая алгоритм, должна отслеживать два числовых значения, а и b. Поэтому предположим, что эти числа хранятся в двух регистрах с такими же именами. Основные требуемые операции - это проверка, не является ли содержимое регистра b нулем, и вычисление остатка от деления содержимого регистра a на содержимое регистра b. Операция вычисления остатка - сложный процесс, однако пока что предположим, что у нас есть элементарное устройство, вычисляющее остатки. В каждом цикле алгоритма вычисления НОД содержимое регистра a требуется заменить содержимым регистра b, а содержимое регистра b следует заменить на остаток от деления старого содержимого a на старое содержимое b. Было бы удобно, если бы можно было



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