Скачиваний:
39
Добавлен:
01.05.2014
Размер:
195.07 Кб
Скачать

15.4. Очереди

Интересным применением разностных списков является организация очередей. Очередь-структура данных для хранения списков, используемая по принципу: «первым записан -первым считан». Голова разностного списка представляет начало очереди, хвост - ее конец, а объекты разностного списка - это элементы очереди. Очередь пуста, если пуст разностный список, т.е. голова и хвост тождественны.

Управление очередью отличается от управления описанным выше справочником. Рассмотрим отношение queue(S),предназначенное для обработки потока команд, представленных списком S.Существуют две основные операции с очередью -включение элемента в очередь и исключение элемента из очереди, представляемые структурами enqueue(X)и dequeue(X)соответственно, где Х - интересующий нас элемент.

В программе 15.11эти операции реализуются абстрактно. Предикат queue (S) обращается к предикату queue (S,Q),где Q-первоначально пустая очередь. Предикат queue/2является интерпретатором для потока команд включения и исключения. Он воспринимает каждую команду и соответственно изменяет состояние очереди. При включении элемента используется незаполненность хвоста очереди; произво­дится конкретизация его новым элементом и соответствующее обновление хвоста

queue(S)

S- последовательность операций включения элементов в очередь и исключения элементов из очереди, представленных списком термов enqueue (Х)и dequeue(X).

queue(S)  queue(S,Q\Q).

queue([enqueue(X)|Xs],Q) 

enqueue(X,Q,Q1),queue(Xs,Q1).

queue([dequeue(X)|Xs],Q) 

dequeue(X,Q,Ql),queue(Xs,Ql).

queuc([ ],Q).

enqueue(X.Qh\[X|Qt],Qh\Qt).

dequeue[X,[X | Qh]\Qt,Qh\Qt).

Программа 15.11.Выполнение операций над очередью.

очереди. Понятно, что отношения enqueueи dequeueмогут быть и неразвернутыми, что дает более выразительную и эффективную, однако более трудную для чтения программу.

Выполнение программы завершается, когда исчерпывается поток команд. Эта программа может быть расширена так, чтобы после выполнения последней команды очередь становилась пустой; для этого один базисный факт можно заменить следующим предложением:

queue([ ].Q)  empty(Q).

Очередь пуста, если ее голова и хвост конкретизируются пустыми списками, выраженными фактом empty([ ]\[ ]).Логически для этого должно быть достаточно предложения empty (Х\Х).однако из-за отсутствия в Прологе контроля на вхождение, рассмотренного в гл. 4,оно может быть удовлетворено ошибочно на непустой очереди, образующей некоторую циклическую структуру данных.

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

flatten (Xs,Ys)

Ys-раскрытый список, содержащий элементы из списка Xs.

flatten(Xs,Ys)  flatten_q(Xs,Qs\Qs,Ys).

flatten_q([X Xs],Ps\[Xs| Qs],Ys) 

flatten_q(X.Ps\Qs.Ys),flatten_q(X,[Q| Ps]\Qs,[X! Ys]) 

constant(X).X  [ ],flatten_q(Q,Ps\Qs,Ys).

flatten._q([ ].[Q | Ps] \Qs,Ys) 

flatten q(Q.Ps\Qs,Ys).

flatten„q([ ].[ ]\[ ],[ ]).

Программа 15.12.Программа раскрытия списка с использованием очереди.

Основное отношение в программе flatten_q(Ls,Q,Xs),где Ls – список списков, подлежащий раскрытию, Q-очередь списков, ожидающих раскрытия, и Xs-список элементов в Ls.Первое предложение flatten/i3посредством отношения flatten/2 инициализирует пустую очередь. Базовой операцией являются включение в очередь хвоста списка и рекурсивное раскрытие головы списка:

flatten. q([X|Xs].Q,Ys) enqueue(Xs,Q,Ql),flatten_q(X,Ql,Ys).

Явно pacкрывая отношение enqueue получим

flatten_q([X | Xs],Qh\ [Xs | Qt].Ans)

flatten_q(X.Qh\Qt,Ys).

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

flatten_q(X,[Q|Qh] \Qt,[X | Ys])  constant(X).X  [ ],flatten_q(Q,Qh \,Qt,Ys).

Когда при раскрытии встречается пустой список, либо верхний элемент исключается из очереди

flatten_q([ ].[Q | Qh],Qt,Ys)  flatten_q(Q,Qh\Qt,Ys),

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

Рассмотрим операционный смысл программы 15.11.В процессе предполагаемого использования очереди сообщения enqueue (X)передаются с определенными X,аdeqiieue(X)-cнеопределенными X.Пока в очередь включено больше элементов, чем из нее исключено, очередь ведет себя нормально; разность между головой очереди и ее хвостом дает элементы, содержащиеся в очереди. Однако, если число полученных команд исключения из очереди превышает число команд включения элементов в очередь, происходит интересное явление - содержимое очереди становится «отрицательным». Голова опережает хвост очереди, что приводит к появлению в очереди отрицательной последовательности неопределенных элементов.

Число таких элементов определяется числом избыточных операций исключения из очереди.

Интересно понаблюдать, как такое поведение согласуется с ассоциативностью присоединения разностных списков. Если очередь Qs\[X1,X2,X3\ Qs],содержащая три неопределенных отрицательных элемента, присоединяется к очереди [а,b,c,d,e\Xs]\Xs,состоящей из пяти элементов, то результатом присоединения будет очередь [d,e\Xs]\Xs сдвумя элементами, где отрицательные элементыХ1,Х2,ХЗ инфицируются са,b,с.

15.5. Дополнительные сведения

Разностные списки сразу вошли в фольклор логического программирования. Первое их писание в литературе дали Clarkи Tarnlund (1977).

Описание метода автоматического преобразования программ без разностных списков в рограммы с разностными списками можно найти в (Bloch, 1984).

Элегантную процедуру поиска lookupна упорядоченных двоичных деревьях описал Warren .(1980).Эта процедура, как будет показано в гл.23, используется в качестве центрального метода при написании компиляторов на Прологе. Van Emden (1984)и Lloyd (1984)подвели определенную теоретическую базу под процедуру управления справочниками и очередями, Осматривая ее как некоторый бесконечный процесс.

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

Соседние файлы в папке prolog14_end