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


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




[81]

(define ql (make-queue))

(insert-queue! ql a) ((a) a)

(insert-queue! ql b) ((a b) b)

(delete-queue! ql)

((b) b)

(delete-queue! ql)

(() b)

«Ничего не работает! - жалуется он. - Ответ интерпретатора показывает, что последний элемент попадает в очередь два раза. А когда я оба элемента уничтожаю, второе b по-прежнему там сидит, так что очередь не становится пустой, хотя должна бы». Ева Лу Атор говорит, что Бен просто не понимает, что происходит. «Дело не в том, что элементы два раза оказываются в очереди, - объясняет она. - Дело в том, что стандартная лисповская печаталка не знает, как устроено представление очереди. Если ты хочешь, чтобы очередь правильно печаталась, придется написать специальную процедуру распечатки очередей». Объясните, что имеет в виду Ева Лу. В частности, объясните, почему в примерах Бена на печать выдается именно такой результат. Определите процедуру print-queue, которая берет на входе очередь и выводит на печать последовательность ее элементов.

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

Вместо того, чтобы представлять очередь как пару указателей, можно построить ее в виде процедуры с внутренним состоянием. Это состояние будет включать указатели на начало и конец обыкновенного списка. Таким образом, make-queue будет иметь вид

(define (make-queue) (let ((front-ptr ...) (rear-ptr ...)) (определения внутренних процедур) (define (dispatch m) ...) dispatch))

Закончите определение make-queue и реализуйте операции над очередями с помощью этого представления.

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

Дек (deque, double-ended queue, «двусторонняя очередь») представляет собой последовательность, элементы в которой могут добавляться и уничтожаться как с головы, так и с хвоста. На деках определены такие операции: конструктор make-deque, предикат empty-deque?, селекторы front-deque и rear-deque, и мутаторы frontinsert-deque! , rear-insert-deque! , front-delete-deque! и rear-delete-deque! . Покажите, как представить дек при помощи пар, и напишите реализацию операций.23Все операции должны выполняться за в(1) шагов.

23 Осторожно, не заставьте ненароком интерпретатор печатать циклическую структуру (см. упр. 3.13).


table

V

*table*

a 1 b 2 c 3

Рис. 3.22: Таблица, представленная в виде списка с заголовком.

3.3.3 Представление таблиц

Когда в главе 2 мы изучали различные способы представления множеств, то в разделе 2.3.3 была упомянута задача поддержания таблицы с идентифицирующими ключами. При реализации программирования, управляемого данными, в разделе 2.4.3, активно использовались двумерные таблицы, в которых информация заносится и ищется с использованием двух ключей. Теперь мы увидим, как такие таблицы можно строить при помощи изменяемых списковых структур.

Сначала рассмотрим одномерную таблицу, где каждый элемент хранится под отдельным ключом. Ее мы реализуем как список записей, каждая из которых представляет собой пару, состоящую из ключа и связанного с ним значения. Пары связаны вместе в список при помощи цепочки пар, в каждой из которых car указывают на одну из записей. Эти связующие пары называются хребтом (backbone) таблицы. Для того, чтобы у нас было место, которое мы будем изменять при добавлении новой записи, таблицу мы строим как список с заголовком (headed list). У такого списка есть в начале специальная хребтовая пара, в которой хранится фиктивная «запись» - в данном случае произвольно выбранный символ *table*. На рисунке 3.22 изображена стрелочная диаграмма для таблицы

a: 1 b: 2 c: 3

Информацию из таблицы можно извлекать при помощи процедуры lookup, которая получает ключ в качестве аргумента, а возвращает связанное с ним значение (либо ложь, если в таблице с этим ключом никакого значения не связано). Lookup определена при помощи операции assoc, которая требует в виде аргументов ключ и список записей. Обратите внимание, что assoc не видит фиктивной записи. Assoc возвращает запись, которая содержит в car искомый ключ.24 Затем lookup проверяет, что запись, возвращенная assoc, не есть ложь, и возвращает значение (то есть cdr) записи.

(define (lookup key table)

(let ((record (assoc key (cdr table))))

24Поскольку assoc пользуется equal?, в качестве ключей она может распознавать символы, числа и списковые структуры.


(if record

(cdr record)

false)))

(assoc key records) ((null? records) false)

((equal? key (caar records)) (car records)) (else (assoc key (cdr records)))))

Чтобы вставить в таблицу значение под данным ключом, сначала мы с помощью assoc проверяем, нет ли уже в таблице записи с этим ключом. Если нет, мы формируем новую запись, «сconsивая» ключ со значением, и вставляем ее в начало списка записей таблицы, после фиктивной записи. Если же в таблице уже была запись с этим ключом, мы переставляем cdr записи на указанное новое значение. Заголовок таблицы используется как неподвижное место, которое мы можем изменять при порождении новой записи.25

(define (insert! key value table)

(let ((record (assoc key (cdr table)))) (if record

(set-cdr! record value) (set-cdr! table

(cons (cons key value) (cdr table)))))

ok)

Для того, чтобы создать таблицу, мы просто порождаем список, содержащий символ *table*:

(define (make-table) (list *table*))

Двумерные таблицы

В двумерной таблице каждое значение индексируется двумя ключами. Такую таблицу мы можем построить как одномерную таблицу, в которой каждый ключ определяет подтаблицу. На рисунке 3.23 изображена стрелочная диаграмма для таблицы

math:

+ : 43

-: 45

*: 42 letters:

a: 97

b: 98

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

25Таким образом, первая хребтовая пара является объектом, который представляет «саму» таблицу; то есть, указатель на таблицу - это указатель на эту пару. Таблица всегда начинается с одной и той же хребтовой пары. Будь это устроено иначе, пришлось бы возвращать из insert! новое начало таблицы в том случае, когда создается новая запись.

(define (cond



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