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

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

те же предикаты 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

Источник: https://studfile.net/preview/16708744/