Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ФиЛП_материалы / Материалы / Prolog / ПособиеПролог.doc
Скачиваний:
55
Добавлен:
01.06.2015
Размер:
449.02 Кб
Скачать

5.2. Организация стеков и очередей

В Прологе, как и в процедурном программировании, списки могут служить основой для построения стеков и очередей.

Начнем со стека. Известно, что для него должны быть определены три операции: добавление элемента в вершину стека, выталкивание элемента из вершины, проверка стека на наличие элементов.

Рассмотрим следующий предикат:

stack(Top, Stack, [Top|Stack]) % ( i, i,o) (o, o ,i)

Здесь Top – элемент, находящийся в вершине стека, второй и третий аргументы – списки, выражающие состояние стека до и после выполнения операции.

Предположим, стек работает с целыми числами и текущее состояние его [1, 2, 3]. Тогда третий аргумент конкретизируется списком с новым состоянием стека.

stack(10, [1,2,3], R).

R=[10,1,2,3]

Предикат, в котором первые два аргумента свободны, а третий конкретизирован текущим состоянием стека, выдаст следующее решение

stack( T, S, [ 8, 2, 13 ] ).

T= 8, S= [ 2, 13]

Проверка стека на наличие элементов выполняется предикатом

empty_stack( [ ] ).

Таким образом, организация стека на основе списка несложна. Чуть более сложной является организация очереди, где новый элемент заносится в ее конец. Для этого воспользуемся рекурсивной процедурой queue ( E, Q, Qnew), где

Q – список – текущее состояние очереди,

Qnew – список-очередь после добавления элемента E.

queue( E, [ ], [ E ] ).

queue( E, [ H|T ], [ H|Tnew ] ):- queue( E, T, Tnew).

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

Считывание 1-го элемента можно реализовать с помощью предиката

dequeue(E, [E|T], T).

Программа для реализации обеих структур выглядит следующим образом:

DOMAINS

list=integer*

PREDICATES

% стек

empty_stack(list).

stack(integer, list, list).

%очередь

empty_queue(list).

enqueue(integer, list, list).

dequeue(integer, list, list).

CLAUSES

% стек

empty_stack([]).

stack(Top, Stack, [Top|Stack]).

% очередь

empty_queue([]).

enqueue(E, [], [E]).

enqueue(E, [H|T], [H|Tnew]):- enqueue(E, T, Tnew).

dequeue(E, [E|T], T).

GOAL

stack(T, S, [1,2,3]). % T=1 S=[2, 3]

stack(0, [1,2], R). % R=[0, 1,2]

enqueue(1, [0,5], R). %R=[1,5,0]

S=[1,2,3], dequeue (X, S, T).% X=1 T=[2,3]

6. Внутренняя база фактов

До сих пор мы рассматривали факты как часть исходной программы. Между тем, в Прологе имеется возможность добавлять факты в программу и удалять их в процессе ее выполнения. Шаблон таких фактов должен быть объявлен в разделе facts (устаревшее, но еще действующее в VIP название этого раздела – database) , который должен располагаться перед разделом предикатов. Факты из этого раздела хранятся в оперативной памяти отдельно от остальных предложений программы и образуют так называемую внутреннюю базу данных. (Кроме внутренней, в VIP может быть организована и внешняя база данных [5].) Действия над фактами, хранящимися во внутренней базе, осуществляются так же, как и над фактами исходной программы, но операции над ними выполняются быстрее. Иногда внутреннюю базу данных называют динамической. До начала работы программы внутренняя база фактов пуста.

Стандартные предикаты для добавления и удаления информации из базы следующие:

asserta(Predicat) – заносит новый факт во внутреннюю базу и помещает его перед имеющимися фактами этой базы;

assertz(Predicat) – заносит новый факт в динамическую БД и помещает его после всех имеющихся там фактов.

(Так же, как assertz действует предикат assert.)

Пример.

FACTS

студент(string, string)

PREDICATES

/* . . . */

asserta(студент("Сидоров","А-87")).

Объект Predicat, являющийся параметром предикатов asserta и assertz, может быть определен в разделе clauses либо в явном виде, либо может быть сформирован на основе данных, извлеченных из предикатов раздела clauses.

Пример. Из программы, содержащей сведения о студентах, поместить во внутреннюю базу факты, содержащие информацию только о студентах, имеющих хотя бы одну двойку:

DOMAINS

фамилия, группа, предмет=string

отметка=integer

FACTS

двоечник(фамилия, группа)

PREDICATES

студент (фамилия, группа, предмет, отметка)

CLAUSES

студент("Петров","А-97","ООП",3).

...

добавить_к_БД :- студент(Name, Grupp, Subject,

Ot), Ot=2, assertz (двоечник(Name, Grupp)), fail.

добавить_к_БД:-!.

GOAL

добавить_к_БД.

(Правило «добавить_к_БД:-!.» необязательно и введено только для того, чтобы одноименное правило было доказанным; если его не будет, то из-за наличия fail правило всегда будет неуспешным.)

Предикат retract(Predicat) используется для удаления фактов из базы. Рассмотрим следующие применения предиката:

- удалить первый факт базы:

retract(двоечник("Сидоров", "А-87")).

- удалить все утверждения, связанные с конкретным предикатом:

удалить_из_DB:- retract(двоечник(_, _, _)), fail.

- полностью очистить содержимое базы:

удалить_DB :- retract( _ ), fail.

(В последнем случае целесообразно применять системный предикат retractall.)

Для внутренней базы фактов предусмотрена возможность обмена с дисковой памятью.

Предикат save(FileName) записывает все факты, содержащиеся в базе, в текстовый файл с именем FileName.

Предикат consult(FailName) загружает в оперативную память все факты, хранящиеся в файле с именем FileName. Предикат считывает данные из файла в том же формате, в котором они были занесены предикатом save.

Пример. Из файла auto.txt, хранящего факты об автомобилях, таких как в программе подразд. 3.2, выбрать все записи и поместить их во внутреннюю базу, исправить значение аргумента «Год» в каждой записи и перенести исправленную информацию в новый файл.

DOMAINS

марка= symbol

год= integer

FACTS

auto(марка,год)

PREDICATES

edit

run

CLAUSES

edit :- auto(Name, Year),

write(Name), write(Year),

retract(auto(Name,Year)),

write("Введите новыйгод"), nl, readint(X), asserta(auto(Name,X)), fail.

edit:-!.

run:-consult("d:\\auto.txt"),edit,

save("d:\\auto1.txt").

GOAL

run.

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

facts – mydatabase1.

Наличие нескольких баз приводит к необходимости использовать предикаты assert, retract, consult и save не с одним, а с двумя аргументами; второй – имя соответствующего раздела.

assert(…., mydatabase1).

Отметим, что если в программе существует одна безымянная база фактов, то она должна быть объявлена с ключевым словом global:

GLOBAL FACTS

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