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


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




[80]

Операция (define

q (make-queue)

(insert-queue! q a) (insert-queue! q b) (delete-queue! q) (insert-queue! q c) (insert-queue! q d) (delete-queue! q)

Результат a

ab b

bc

bcd

cd

Рис. 3.18: Операции над очередью.

•конструктор (make-queue) возвращает пустую очередь (очередь, в которой нет ни одного элемента).

•два селектора: (empty-queue? (очередь)) проверяет, пуста ли очередь, (front-queue (очередь)) возвращает объект, находящийся в голове очереди. Если очередь пуста, он сообщает об ошибке. Очередь не модифицируется.

•Два мутатора: (insert-queue! (очередь) (элемент)) вставляет элемент в хвост очереди и возвращает в качестве значения измененную очередь; (delete-queue! (очередь)) удаляет элемент в голове очереди и возвращает в качестве значения измененную очередь. Если перед уничтожением элемента очередь оказывается пустой, выводится сообщение об ошибке.

Поскольку очередь есть последовательность элементов, ее, разумеется, можно было бы представить как обыкновенный список; головой очереди был бы car этого списка, вставка элемента в очередь сводилась бы к добавлению нового элемента в конец списка, а уничтожение элемента из очереди состояло бы просто во взятии cdr списка. Однако такая реализация неэффективна, поскольку для вставки элемента нам пришлось бы просматривать весь список до конца. Поскольку единственный доступный нам метод просмотра списка - это последовательное применение cdr, такой просмотр требует 0(n) шагов для очереди с n членами. Простое видоизменение спискового представления преодолевает этот недостаток, позволяя нам реализовать операции с очередью так, чтобы все они требовали 0(1) шагов; то есть, чтобы число шагов алгоритма не зависело от длины очереди.

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

Очередь, таким образом, представляется в виде пары указателей, front-ptr и rear-ptr, которые обозначают, соответственно, первую и последнюю пару обыкновенного списка. Поскольку нам хочется, чтобы очередь была объектом с собственной индивидуальностью, соединить эти два указателя можно с помощью cons, так что собственно очередь будет результатом cons двух


front-ptr

rear-ptr

Т •--* t

Рис. 3.19: Реализация очереди в виде списка с указателями на начало и конец.

q

b

a

c

указателей. Такое представление показано на рис. 3.19.

Во время определения операций над очередью мы пользуемся следующими процедурами, которые позволяют нам читать и записывать указатели на начало и конец очереди:

(define (front-ptr queue) (car queue)) (define (rear-ptr queue) (cdr queue))

(define (set-front-ptr! queue item) (set-car! queue item))

(define (set-rear-ptr! queue item) (set-cdr! queue item))

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

(define (empty-queue? queue) (null? (front-ptr queue)))

Конструктор make-queue возвращает в качестве исходно пустой очереди пару, в которой и car, и cdr являются пустыми списками:

(define (make-queue) (cons () ()))

При обращении к элементу в голове очереди мы возвращаем car пары, на которую указывает головной указатель:

(define (front-queue queue) (if (empty-queue? queue)

(error "FRONT вызвана с пустой очередью" queue) (car (front-ptr queue))))

Чтобы вставить элемент в конец очереди, мы используем метод, результат которого показан на рисунке 3.20. Первым делом мы создаем новую пару, car которой содержит вставляемый элемент, а cdr - пустой список. Если очередь была пуста, мы перенаправляем на эту пару и головной, и хвостовой указатели. В противном случае, мы изменяем последнюю пару очереди так, чтобы следующей была новая пара, и хвостовой указатель тоже перенаправляем на нее же.

(define (insert-queue! queue item) (let ((new-pair (cons item ())))


front-ptr

rear-ptr

Т •--* ?

b

Рис. 3.20: Результат применения (insert-queue! q d) к очереди с рисунка 3.19

q

d

a

c

front-ptr

T •--* t

b

rear-ptr

Рис. 3.21: Результат применения (delete-queue! q) к очереди с рис. 3.20.

q

d

a

c

(cond ((empty-queue? queue)

(set-front-ptr! queue new-pair) (set-rear-ptr! queue new-pair) queue)

(else

(set-cdr! (rear-ptr queue) new-pair) (set-rear-ptr! queue new-pair)

queue))))

Чтобы уничтожить элемент в голове очереди, мы просто переставляем головной указатель на второй элемент очереди, а его можно найти в cdr первого элемента (см. рис. 3.21):22

(define (delete-queue! queue) (cond ((empty-queue? queue)

(error "DELETE! вызвана с пустой очередью" queue))

(else

(set-front-ptr! queue (cdr (front-ptr queue))) queue)))

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

Бен Битобор решает протестировать вышеописанную реализацию. Он вводит процедуры в интерпретаторе Лиспа и тестирует их:

22В случае, если первый элемент - одновременно и последний, после его уничтожения головной указатель окажется пустым списком, и это будет означать, что очередь пуста; нам незачем заботиться о хвостовом указателе, который по-прежнему будет указывать на уничтоженный элемент, поскольку empty-queue? смотрит только на голову.



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