Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Волченков Логическое программирование язык пролог 2015

.pdf
Скачиваний:
11
Добавлен:
12.11.2022
Размер:
4.14 Mб
Скачать

те же предикаты assert/1 и retract/1, менять значение этой переменной, уничтожить её, когда необходимость в ней исчезнет.

Ниже (код 8.2) приводятся определения некоторых полезных предикатов для работы с такими глобальными переменными (естественно, можно придумать и еще какие-нибудь):

ini_bag/2 – создать переменную с данным именем и данным начальным значением,

ini_bag/1 – создать переменную с данным именем и пустым списком в качестве начального значения,

dob_bag/3 – добавить в список новый элемент, выдать старое значение списка,

sum_bag/3 – прибавить к числовому значению переменной данное число, выдать старое значение,

inc_bag/2 – прибавить единицу, выдать старое значение, dec_bag/2 – вычесть единицу, выдать старое значение, zam_bag/3 – заменить старое значение новым,

fin_bag/2 – уничтожить переменную с данным именем и выдать

ее значение.

Код 8.2

ini_bag(Bag, Value) :- assert(bag(Bag, Value)). ini_bag(Bag) :- assert(bag(Bag, [])).

dob_bag(Bag, New, Old) :- retract(bag(Bag, Old)), assert(bag(Bag, [New|Old])), !.

sum_bag(Bag, N, Old) :- retract(bag(Bag, Old)), New is Old + N, assert(bag(Bag, New)), !.

inc_bag(Bag, Old) :- retract(bag(Bag, Old)), inc(Old, New), assert(bag(Bag, New)), !.

111

dec_bag(Bag, Old) :- retract(bag(Bag, Old)), dec(Old, New), assert(bag(Bag, New)), !.

zam_bag(Bag, New, Old) :- retract(bag(Bag, Old)), assert(bag(Bag, New)), !.

fin_bag(Bag, Value) :- retract(bag(Bag, Value)).

Пример 8.2. В качестве примера применения глобальных переменных рассмотрим задачу обработки отношения, представленного набором фактов, с целью получения некоторых характеристик этого отношения, в частности:

максимального значения данного числового аргумента,

суммы значений данного числового аргумента,

списка значений данного нечислового аргумента.

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

rel_str(книга(шифp(_), автоp(_), название(_),

томов(_), том(_), стpаниц(_), год(_), издательство(_), гоpод(_), аннотация(_))).

Анонимные переменные в этой структуре изображают «ячейки памяти», в которых могут храниться какие-нибудь сведения об аргументах, например их тип, шаблон или что-то еще.

Пусть отношение содержит следующие факты (код 8.3):

Код 8.3

книга(7008991, 'Пушкин А.С.', 'Евгений Онегин. Поэмы', 1, 1, 368, 1970, 'Художественная Литература', 'Москва', _).

книга(7805540, 'Пушкин А.С.', 'Стихотворения. Поэмы', 1, 1, 416, 1978, 'Русский язык', 'Москва', _).

112

книга(8606115, 'Булгаков М.А.', 'Мастеp и Маpгаpита', 1, 1, 416, 1986, 'Лиесма', 'Рига', _).

книга(8711803, 'Достоевский Ф.М.', 'Подpосток',

1, 1, 576, 1987, 'Московский pабочий', 'Москва', _).

книга(9009366, 'Достоевский Ф.М.', 'Бесы',

1, 1, 672, 1990, 'Художественная литеpатуpа', 'Москва', _).

книга(9110577, 'Достоевский Ф.М.', 'Бpатья Каpамазовы', 2, 1, 416, 1991, 'Пpавда', 'Москва', _).

книга(9110578, 'Достоевский Ф.М.', 'Бpатья Каpамазовы',

2, 2, 529, 1991, 'Пpавда', 'Москва', _).

Допустим, что нужно найти книгу с максимальным числом страниц, общее число страниц во всех имеющихся книгах, а также список авторов всех книг.

Эту задачу легко решить на Прологе с использованием представленных выше глобальных переменных. Для этого создадим три глобальные переменные с именами max, itog и avtor_list.

Начальные значения этих переменных (_, 0), 0, []. Их конечные значения:

пара, состоящая из шифра книги с максимальным числом страниц и этого числа;

общее число страниц всех книг;

список авторов всех книг.

Программа на Прологе для решения данной задачи такова:

Код 8.4

itogi(Res1, Res2, Res3):-

ini_bag(max, (_,0)), ini_bag(itog, 0), ini_bag(avtor_list),

cycle,

fin_bag(max, Res1), fin_bag(itog, Res2), fin_bag(avtor_list, List),

sort(List, Res3). cycle:-

'книга'(A1, A2, _, _, _, A6, _, _, _, _), max(A1, A6),

113

itog(A6), avtor_list(A2), fail.

cycle :- !.

max(Sh, Str):- bag(max, (_, Old)), Str > Old, !,

zam_bag(max, (Sh, Str), (_, Old)). max(_,_):-!.

itog(Str):- sum_bag(itog, Str, _).

avtor_list(Avt):- dob_bag(avtor_list, Avt, _).

Обратившись к Прологу с запросом

?- itogi(Res1, Res2, Res3).

для приведенного множества книг получим следующее решение

данной задачи:

Res1 = (9009366, 672) Res2 = 3393

Res3 = ['Булгаков М.А.', 'Достоевский Ф.М.', 'Пушкин А.С.'].

Отметим, что в данной программе используется возвратный способ организации цикла cycle – с помощью предиката fail. При этом предикаты max/2, itog/1, avtor_list/1 должны работать безвозвратно, а по возврату происходит обращение лишь к фактам книга/10 с просмотром всего этого отношения.

4. Создание утилит для работы с базой данных

В приведённом выше примере очевидна одна негативная особенность: при написании определения предиката cycle программист был вынужден обращаться к довольно громоздкой структуре отношения, выделяя нужные аргументы, заключенные в ее глубине. Было бы еще хуже, если бы запросы к библиотечной базе дан-

114

ных понадобилось составлять непрограммирующему пользователю.

Пример 8.3. Рассмотрим следующие запросы к библиотечной базе данных:

«Найти авторов книг, изданных в городе Рига»,

«Указать названия, авторов и издательства двухтомных изданий»,

«Какие книги Пушкина А.С., изданные до 1990 года, имеются в базе данных?»,

«Книги каких немосковских издательств имеются в базе дан-

ных?».

Пользователь вынужден указывать те места в структуре книга/10, в которых находятся интересующие его атрибуты.

Естественным желанием программиста – составителя определений на Прологе – является освободить пользователя от необходимости знать структуру отношения и предоставить ему полезные утилиты для работы с базой данных.

Следующая программа (код 8.5) демонстрирует возможность

работы с такого рода утилитами.

Код 8.5

book_menu:-

book_attrib(L), book_menu(L).

book_menu([]):-

write('Шифр - шифр книги (ключ отношения)'), !.

book_menu([A|T]):- write(A), nl, book_menu(T).

book_attrib(['автоp(Шифр, Автор)',

'название(Шифр, Название)', 'томов(Шифр, КоличТомов)', 'том(Шифр, Том)', 'стpаниц(Шифр, ЧисСтраниц)', 'год(Шифр, Год)',

'издательство(Шифр, Издательство)',

115

'гоpод(Шифр, Город)',

 

'аннотация(Шифр, Аннотация)']).

book(X):-

%

functor(X, книга, 10), call(X).

Утилиты для пользователя:

автоp(Sh, Av) :- book(X), arg(1, X, Sh), arg(2, X, Av). название(Sh, Naz) :- book(X), arg(1, X, Sh), arg(3, X, Naz). томов(Sh, TT) :- book(X), arg(1, X, Sh), arg(4, X, TT). том(Sh, T) :- book(X), arg(1, X, Sh), arg(5, X, T). стpаниц(Sh, Str) :- book(X), arg(1, X, Sh), arg(6, X, Str). год(Sh, God) :- book(X), arg(1, X, Sh), arg(7, X, God). издательство(Sh, Izd) :- book(X), arg(1, X, Sh), arg(8, X, Izd). гоpод(Sh, Gor) :- book(X), arg(1, X, Sh), arg(9, X, Gor). аннотация(Sh, Ann) :- book(X), arg(1, X, Sh), arg(10, X, Ann).

С использованием утилит перечисленные выше примеры запросов пользователя к библиотечной базе данных легко записать в виде следующих целей Пролога:

?- город(S, 'Рига'), автор(S, X).

?- томов(S, 2), название(S, N), автор(S, A), издательство(S, I).

?- автор(S, 'Пушкин А.С.'), год(S, G), G < 1990,

название(S, N).

?- город(S, G), G =\= 'Москва', издательство(S, I).

5. Интерпретация операций реляционной алгебры

Рассмотрим возможности реализации операций реляционной алгебры средствами Пролога.

Как известно, основными операциями реляционной алгебры яв-

ляются: проекция, соединение, пересечение и объединение отноше-

ний. Ограничимся операцией проекции - выделением части атрибутов отношения с исключением появляющихся при этом повторений.

116

Пример 8.4. Рассмотрим уже имеющееся отношение книга/10. Проекцией этого отношения на атрибуты город и издательство

будет новое отношение, которое мы назовем место/2. После исключения повторений в этом отношении будет 5 строк. Его будут представлять следующие факты Пролога:

место('Москва', 'Художественная литеpатуpа'). место('Москва', 'Русский язык').

место('Рига', 'Лиесма').

место('Москва', 'Московский pабочий'). место('Москва', 'Пpавда').

Рассмотрим определение предиката proj/2. Первым аргументом предиката служит спецификация исходного отношения в виде R/N, вторым аргументом – структура проекции в виде P/L, где P – имя нового отношения, а L – список номеров атрибутов исходного отношения, на которые оно проецируется.

Запрос на получение рассмотренной выше проекции следующий:

?- proj(книга/10, место/[9, 8]).

Определение предиката proj/2 таково (код 8.6):

Код 8.6

proj(R/N, P/L):-

functor(S, R, N), proj1(S, P/L).

proj1(S, P/L):- call(S), otobr(S, L, M),

Result =.. [P|M], assert_new_fact(Result), fail.

proj1(_, _):-!.

otobr(_, [], []):-!. otobr(S, [I|L], [X|M]):-

arg(I, S, X), otobr(S, L, M).

assert_new_fact(Fact):- call(Fact), !.

117

assert_new_fact(Fact):- assert(Fact), !.

Напомним, что использование предиката assert_new_fact/1 вместо встроенного предиката assert/1 обеспечивает устранение повторяющихся строк в результирующем отношении (одинаковых фактов Пролога).

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

естественно, лишь с небольшими, «игрушечными» базами. В некоторых реализациях Пролога (например, в системе Atity Prolog, опи-

санной в книге [3]) предусмотрены возможности взаимодействия Пролога с внешними носителями – с файлами, с виртуальной памятью, что позволяет говорить уже не об «игрушечных» примерах, а о реальных дедуктивных базах данных.

6.О статических и динамических предикатах Пролог системы LPA

ВПрологе LPA есть специфика – определяемые предикаты подразделяются на статические и динамические.

По умолчанию, при компиляции новых определений предикатов

спомощью встроенных предикатов consult/1 (перезагрузки) и compile/1 (компиляции) загружаемые и компилируемые предикаты считаются статическими.

С помощью встроенных предикатов assert/1 и retract/1 можно добавлять и удалять определения только динамических предикатов.

Определения статических предикатов могут быть изменены

лишь полным переопределением с помощью встроенных предика-

тов consult/1 и compile/1.

Полное удаление определений как статических, так и динамиче-

ских предикатов может быть произведено с помощью встроенного предиката abolish/1.

Определения статических предикатов можно удалить и другим

способом, объявив эти предикаты динамическими с помощью встроенного предиката dynamic/1.

Взаключение рассмотрим пример на использование динамических и статических предикатов.

118

Пример 8.5. Рассмотрим запись правил и фактов в БД Пролога. Задача такая: с помощью предиката task1 в базу данных запи-

сывается новое правило c( X, Y) :- a( X), b( Y)., позволяющее искать значения переменных X и Y по запросу: ?- c(X, Y).

Код 8.7

% Запись факта и правила в БД Пролога

:- dynamic( b/1).

a( Петя). a( Вася). b( Маша).

task1 :- assert( (c( X, Y) :- (a( X), b( Y)))). task2 :- assert( b( Даша)).

%Если несколько раз отрабатывать цель ?- task2.

%то в БД появятся несколько фактов b( Даша).

%Избежать этого можно, заменив assert/1 на assert_new_fact/1:

assert_new_fact( F) :- call( F), !. assert_new_fact( F) :- !, assert( F).

Комментарий. Для снятия блокировки при попытке записать новый факт b(Даша) в базу данных Пролога, когда в ней уже есть факт b(Маша), используется директива :- dynamic( b/1).

Следующая «фотография» консоли (код 8.8) достаточно красно-

речиво описывает результаты экспериментов с данной программой.

Код 8.8

| ?- task1. yes

| ?- bagof((X,Y), c(X,Y), L). X = _ ,

Y = _ ,

L = [(Петя,Маша),(Вася,Маша)]

| ?- task2. yes

| ?- bagof((X,Y), c(X,Y), L).

119

X = _ ,

Y = _ ,

L = [(Петя,Маша),(Петя,Даша),(Вася,Маша),(Вася,Даша)]

| ?- assert_new_fact(b(Глаша)). yes

| ?- bagof((X,Y), c(X,Y), L). X = _ ,

Y = _ ,

L = [(Петя,Маша),(Петя,Даша),(Петя,Глаша),(Вася,Маша), (Вася,Даша), (Вася,Глаша)]

| ?- assert_new_fact(b(Даша)). yes

| ?- bagof((X,Y), c(X,Y), L). X = _ ,

Y = _ ,

L = [(Петя,Маша),(Петя,Даша),(Петя,Глаша),(Вася,Маша), (Вася,Даша),(Вася,Глаша)]

| ?- task2. yes

| ?- bagof((X,Y), c(X,Y), L). X = _ ,

Y = _ ,

L = [(Петя,Маша),(Петя,Даша),(Петя,Глаша),(Петя,Даша),

(Вася,Маша),(Вася,Даша),(Вася,Глаша),(Вася,Даша)]

| ?-

Отметим, что попытка записать в базу данных новый факт с помощью цели ?- assert_new_fact(a(Дима)). потерпела бы фиаско, так как предикат a/1 в отличие от предиката b/1 является не динамическим, а статическим.

120

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]