13.4. Модуль очереди.
Очередь – это полезная структура данных для хранения информации в упорядоченной последовательности, подобно человеческой очереди, когда люди стоят один за другим. Очередь символов будет организована в виде модуля, реализованного двумя различными способами, и использована для решения задачи удаления лишних пробелов из текста.
Переменная типа TEXT предоставляет возможность хранения и извлечения символов в последовательности с использованием операторов READ/WRITE. Переменные типа TEXT и соответствующие операции над ними соблюдают правила программного модуля, потому что единственным способом сохранения и извлечения данных является использование операторов READ/WRITE.
Очередь – это подобное средство хранения и извлечения данных. Она работает по принципу FIFO (first-in, first-out): элементы добавляются в хвост очереди и извлекаются из головы очереди. Таким образом, очередь похожа на файл за исключением того, что добавления и удаления могут чередоваться в произвольном порядке.
Например, рассмотрим очередь, элементами которой являются символы. Типичный набор операций включает:
Операция |
Результат |
EmptyQ |
Инициализация очереди - очистка |
AddQ(VAR Elt: CHAR) |
Добавляет элемент Elt в очередь |
DelQ |
Удаляет первый элемент из очереди |
HeadQ(Var Elt: CHAR) |
Присваивает Elt в значение первого элемента очереди |
Если очередь содержит один или более элементов, DelQ удаляет первый из очереди, а HeadQ возвращает первый из очереди копированием его в Elt (но не изменяет при этом очередь). Если очередь пуста, DelQ ничего не делает, а HeadQ возвращает #.
В следующих разделах даны две реализации модуля для очереди символов. Первая реализация, названная SlowQueue основывается на очень простом представлении данных, которое не позволяет оптимизаций или повышения эффективности в процедурах модуля. Вторая реализация, FastQueue, использует более сложное представление данных и более сложные процедуры для увеличения скорости выполнения операций. Хотя обе реализации довольно различны внутренне, они имеют абсолютно одинаковое внешнее представление. Единственная разница во времени выполнения. То есть эти два модуля взаимозаменяемы в программах, которые их используют. Когда FastQueue заменяет SlowQueue, не требуется других изменений в программе использующей модуль. Программа изолирована от изменений в модуле, поскольку и программа и модуль соблюдают правила разработки и использования модулей.
13.4.1. SlowQueue
Для первой реализации очереди рассмотрим стратегию управления очередью в единственном текстовом файле, названном Q:
VAR
Q: TEXT;
Пустая очередь представляется файлом, содержащим только маркер строки. Поскольку конец очереди отмечен таким образом, очередь не может содержать внутренние маркеры конца строки. Каждая процедура завершается операцией RESET над файлом, поэтому когда любая процедура начинает выполнение, она считает, что файл готов для чтения.
Добавление в или удаление элемента из очереди требует операторов WRITE, поскольку текстовый файл, представляющий очередь, открыт для чтения, текущее содержимое файла должно быть скопировано во временный файл, а потом обратно в исходный. Набросок модуля очереди с абстрактными комментариями показан ниже.
{модуль очереди}
VAR
Q: TEXT;
{включить PROCEDURE CopyOpen(VAR Fin, Fout: TEXT)
добавляет строку будущего Fin в Fout}
PROCEDURE EmptyQ;
{Queue := <>}
PROCEDURE AddQ(VAR Elt: CHAR);
{Queue := Queue & <Elt>}
PROCEDURE DelQ;
{(Queue = <> -> ) | Queue = <X>&Y -> Queue := Y}
PROCEDURE HeadQ(VAR Elt: CHAR);
{(Queue = <> -> Elt := ‘#’) | Queue = <X>&Y -> Elt := X}
{конец модуля очереди}
Конкретные функции, соответствующие данным абстрактным функциям будут сейчас реализованы.
EmptyQ переписывает Q, как пустую строку. В конкретном комментарии используется символ / для обозначения конца строки.
PROCEDURE EmptyQ;
{Q := <, /, R>}
BEGIN {EmptyQ}
REWRITE(Q);
WRITELN(Q);
RESET(Q)
END; {EmptyQ}
AddQ копирует Q во временный файл и добавляет содержимое Elt, потом временный файл копируется обратно в Q.
PROCEDURE AddQ (VAR Elt : CHAR);
{Q = <,x/,R>,где x строка И Elt = a --> Q = <,xa/,R> }
VAR
Temp: TEXT;
BEGIN {AddQ}
REWRITE(Temp);
CopyOpen(Q,Temp);
WRITELN(Temp,Elt);
{копируем Temp в Elt}
RESET(Temp);
REWRITE(Q);
CopyOpen(Temp,Q);
WRITELN(Q);
RESET(Q)
END {AddQ};
PROCEDURE DelQ;
{(Q = <,/,R> -->)|
(Q = <,ax/,R>,где a символ и x строка -->
Q:= <,x/,R> }
VAR
Ch: CHAR;
BEGIN {DelQ}
{удаляем первый элемент из Q};
READ(Q,Ch);
IF NOT EOF (Q)
THEN {не пустой}
BEGIN
REWRITE(Temp);
CopyOpen(Q,Temp);
WRITELN(Temp);
{копируем Temp в Q}
RESET(Temp);
REWRITE(Q);
CopyOpen(Temp,Q);
WRITELN(Q);
END;
RESET(Q)
END {DelQ};
PROCEDURE HeadQ(VAR Elt: CHAR);
{(Q = <,/,R> --> Elt := '#')|
(Q = <,ax/,R>,где a символ и x строка --> Elt:= 'a' }
BEGIN {HeadQ}
IF NOT EOLN(Q)
THEN
READ(Q,Elt)
ELSE
Elt := '#';
RESET(Q)
END{HeadQ};
