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

Логическое программирование. Учебно-методическое пособие к лабораторным работам

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
61
3) Отсечение не влияет на цели, расположенные правее отсечения в
клаузе. В случае возвратного движения (отката) эти цели могут порождать
более одного решения.
Таким образом, если отсечение стоит первым в теле клаузы, то клауза
функционирует в программе обычным образом. Однако, в случае успешного
выполнения всех целей этой клаузы, клаузы, следующие за ней и имеющие
такую же голову, как бы исключаются из логической программы.
Если же отсечение стоит последним в теле клаузы, то, наряду со сказан-
ным выше, все цели данной клаузы могут привести только к одному реше-
нию.
Влияние промежуточного положения отсечения в теле клаузы определя-
ется всеми тремя следствиями.
По отношению к результатам работы логической программы различают
красное и зелёное отсечения.
Когда отсечение используется только для экономии, с целью преодоле-
ния избыточных, не приводящих к положительному результату ходов по де­реву решений, то отсечение называют зелёным. Главным отличительным
признаком зелёного отсечения является тот факт, что добавление или уда-
ление зелёного отсечения из логической программы не влияет на итоговый
результат работы программы, а влияет только на уменьшение или увеличе-
ние вычислительных затрат. То есть декларативный смысл программы не
меняется.
В отличие от него, красное отсечение всегда меняет результат работы
логической программы. Можно определить красное отсечение как всякое от-
сечение, не являющееся зелёным (по принципу исключенного третьего). Следовательно, всякое красное отсечение меняет декларативный
смысл программы.
Для иллюстрации можно рассмотреть простейшую ситуацию, реализую-
щую ветвление вычислительного процесса. Предлагаемое к рассмотрению
правило rul(X) печатает фразу «первая ветка», если аргумент правила равен
единице, и фразу «вторая ветка», если аргумент правила равен двум. Без
использования отсечения это ветвление может быть организовано так:
62
rul(X):- X=1, write("первая ветка"), nl. rul(X):- X=2, write("вторая ветка"), nl.
Зелёное отсечение оптимизирует ветвление несколькими способами.
а) Отсечение в конце тела клаузы:
rul(X):- X=1, write("первая ветка"), nl, !. rul(X):- X=2, write("вторая ветка"), nl, !.
б) Отсечение в середине тела клаузы:
rul(X):- X=1, !, write("первая ветка"), nl. rul(X):- X=2, !, write("вторая ветка"), nl.
в) Отсечение в начале тела клаузы:
rul(1):- !, write("первая ветка"), nl. rul(2):- !, write("вторая ветка"), nl.
Важно отметить, что отсечение во вторых по порядку правилах никакой
процедурной нагрузки не несёт. Достаточно поставить этот предикат только
в первое правило, так как после последнего правила rul(X) других правил с
такой головой нет. Однако размещение отсечения во все эти правила поз-
воляет сохранить внешний декларативный вид программы, так как в этом случае изменение порядка правил не влияет на результат программы и на её эффективность.
Видно, что первые два варианта правила run(X) с отсечениями в конце и
в середине клаузы мало отличаются друг от друга, чего нельзя сказать о
третьем варианте. При этом во всех трёх случаях отсечение является зелё-
ным, так как удаление отсечения не изменит результат работы программы,
а лишь заставит алгоритм поиска решения совершить лишний заход.
Приведённый ниже Пример_7 демонстрирует организацию вычисления
функции sign(X,Y) с использованием зелёного отсечения. Экономное вычис-
ление нелинейных функций, аналогичных данной, — такой же, как и в пра­виле run(X), типовой случай применения зелёных отсечений.
Пример использования красного отсечения приведён здесь же. Это за-
программированная фраза:
«Мэри любит всех животных, кроме змей».
63
Такая фраза имеет на ПРОЛОГе (в любой его версии) следующий вид:
likes ("Мэри", X) :- snake(X), !, fail.
likes ("Мэри", X) :- animal(X).
В программе используется встроенный предикат fail, означающий гаран-
тированный неуспех. То есть первое по счёту сверху правило likes("Мэри",X) никогда не будет считаться ПРОЛОГ-машиной выполненным. Если предикат
snake(X) будет успешен, то вслед за ним автоматически выполнится отсе-
чение — '!', и затем предикат fail сообщит системе о неуспешности этого
правила. При этом выполненное отсечение исключит из рассмотрения вто-
рое правило.
Порядок клауз в этом случае — обязательный, также как и наличие отсе­чения. Отсутствие отсечения, также как изменение порядка клауз, приведут к из-менению результатов работы программы. Естественно, предполагает-
ся, что змея snake(X) также является животным animal(X), что отражается в
правиле:
animal (X) :- dog(X); cat(X); rat(X); ... ; snake (X).
Сочетание «отсечение — fail», использованное в программе likes(...),
является типичным в организации вычислительного процесса с использова-
нием красного отсечения.
Другой пример — остановка рекурсии с помощью отсечения, упраздня­ющая условие выхода, как при вычислении факториала:
fact(0, 1) :- !. fact(N, F) :- N1=N-1, fact(N1, F1), F=N*F1.
Видно, что условия выхода из рекурсии во втором правиле fact нет.
Это тоже красное отсечение, так как его отсутствие исказит результаты и
приведёт к бесконечной рекурсии.
64
/* Пример 7. Использование "отсечения" - предиката cut (!). */
predicates
sign(real, real) % предикат для вычисления функции знака числа likes(string, string) % предикат "нравится" snake(string) % предикат "змея" cat(string) % предикат "кошка" rat(string) % предикат "крыса" dog(string) % предикат "собака" animal(string) % предикат "животное" fact(integer, integer) % предикат "факториал" p(symbol) % некий предикат p(...) not_p(symbol) % отрицание предиката p(...) - предикат not_p(...)
clauses
/* Пример зелёного отсечения - вычисление функции знака числа X. */ sign(X,1) :- X>0, !. sign(X,0) :- X=0, !. % Порядок клауз sign(.,.) - произвольный. sign(X,-1):- X<0, !.
% Пример красного отсечения: "Мэри любит всех животных, кроме змей".
dog("Полкан"). cat("Пушок"). cat("Мурзик"). rat("Шушара"). snake("Сколопендра"). animal(X) :- dog(X); % X - животное, если это или собака, cat(X); % или кошка, rat(X); % или крыса, snake(X). % или змея. likes("Мэри", X) :- snake(X), !, fail. % Сочетание "отсечение" - fail. likes("Мэри", X) :- animal(X). % Такой порядок клауз - ОБЯЗАТЕЛЬНЫЙ.
/* Пример использование красного отсечения для выхода из рекурсии. */
fact(0, 1) :- !. % Условие выхода: 0!=1. fact(N, F) :- N1=N-1, fact(N1, F1), F=N*F1. % Рекурсивное правило.
% Пример использования красного отсечения - предикат отрицания not_p. p(var). % Аргумент предиката p(X) - некая величина var. not_p(X) :- p(X), !, fail. % Если успешен p(X), то неуспешен not_p(X). not_p(X) :- true. % Если p(X) неуспешен, то успешен not_p(X).
/* Порядок клауз в последнем случае - ОБЯЗАТЕЛЬНЫЙ. */
65
Ещё один вариант использования отсечения — оптимизация остатка
рекурсии (или оптимизация хвостовой рекурсии). Такая оптимизация ис-
пользуется тогда, когда рекурсивное правило является праворекурсивным (см. работу №4). Тогда отсечение, поставленное перед последним предика-
том правила, помогает снизить вычислительные затраты, фактически пре-
вращая рекурсивное правило в итерационное (см. так же работу №4).
Например, отсечение используется для вставки элемента в заданное место списка:
insert_it(Elem, List, List1):
insert_it(X, [Y| Ls], [Y|Ls1]) :- asc_order (X,Y), !, insert_it (X, Ls, Ls1).
insert_it(X, Ls, [X| Ls]). где asc_order(X,Y):- X>Y.
Полученная программа вставки элемента в список может быть исполь-
зована для сортировки списков вставками:
sort_insert([], []).
sort_insert([X| T], Ls) :- sort_insert (T, Ts), insert_it(X, Ts, Ls).
Использует отсечение и праворекурсивное правило удаления отрица-
тельных элементов из списка:
discard_negativ([], []). discard_negativ([H| T], Result) :- H<0, !, discard_negativ(T, Result). discard_negativ([H| T], [H| Result]) :- discard_negativ(T, Result).
С помощью отсечения удобно проиллюстрировать функционирование
встроенного предиката второго порядка not(...). Этот предикат возвращает значение fail («неуспех»), когда предикат, являющийся его аргументом, успешен (возвращает значение true). И, наоборот, предикат not(...) возвра­щает значение true, когда аргумент предиката неуспешен (возвращает зна­чение fail). Отсечение помогает понять процедуру его функционирования:
not(P):- P, !, fail. not(P). или not(P):- P, !, fail; true.
66
Однако написать такую логическую программу пользователь не может,
так как предикат не может быть аргументом предиката. Пример_7 показыва-
ет, как вместо универсального предиката not создать частный предикат not_p, противоположный данному предикату p.
Задания для лабораторной работы №7
Часть 1
Запрограммировать с использованием «зелёного» отсечения нелинейно-
сти:
1. «единичный скачок»;
2. «зону нечувствительности»;
3. «насыщение»;
4. «зону нечувствительности с насыщением»;
5. «реле с зоной нечувствительности»;
6. «модуль»;
7. «модуль с насыщением»;
8. «модуль с зоной нечувствительности»;
9. «модуль с насыщением и зоной нечувствительности»;
10. «гистерезис»;
11. «люфт»;
12. «люфт с насыщением»;
13. «реле»;
14. функцию «прямоугольник» — rect(x);
15. функцию Радемахера;
16. функцию Хэвисайда.
17. функцию «максимум из двух переменных».
18. функцию «минимум из двух переменных».
Часть 2
Запрограммировать с помощью «красного» отсечения следующие фра-
зы:
1. «Богородица молится за всех, кроме царя Ирода».
2. «Нет никого, кроме Бога, без греха».
3. «Прощаются все грехи, кроме греха Иуды» (Данте).
67
4. «Хороши все жанры в искусстве, кроме скучного» (Вольтер).
5. «Все грибы, кроме поганок, съедобны».
6. «Все рыбы, кроме акул, добры к детям».
7. «Маша любит все фрукты, кроме кислых».
8. «Виктор любит все виды спорта, кроме шахмат и шашек».
9. «Игорь любит все уроки, кроме физкультуры».
10. «Вера не любит игровые виды спорта, а остальные любит».
11. «Все игры с мячом — командные, кроме художественной гимнастики».
12. «Даша любит все цвета, кроме чёрного».
13. «Всё покупается, кроме любви».
14. «Юле нравятся любые юбки, кроме мини».
15. «Миша любит все ягоды, кроме горьких».
16. «Света ест любую пищу, кроме мучной и жирной».
17. «Любую потерю можно вернуть, кроме времени и здоровья».
18. «Петя ест любую еду, кроме мясной».
Часть 3
Модифицируйте рекурсивные программы из 4-й, 5-й и 6-й лабораторных
работ, изменив в них условия выхода с использованием «красного» отсече-
ния.
Часть 4
Исследуйте возможности оптимизации хвостовой рекурсии с использо-
ванием отсечения в праворекурсивных программах из 4-й, 5-й и 6-й работ.
Методические указания
Понятие «зелёного» и «красного» отсечения подробно рассмотрено в
[14], а также в гл. 5 [10]. Характерные примеры отсечений есть в гл. 4 [7] и в гл. 11 [8].
Содержание отчёта:
1) заголовок и цель лабораторной работы;
2) индивидуальное задание;
3) тексты логических программ, содержащие отсечение;
4) результаты работы программ и выводы.
68
Р а б о т а №8. ВНУТРЕННИЕ ДИНАМИЧЕСКИЕ
БАЗЫ ДАННЫХ ПРОЛОГа
Цель работы изучение принципов программирования с использова-
нием внутренних динамических баз данных. Безымянная база данных и ба­зы данных с именем (именованные). Знакомство с встроенными предиката­ми ПРОЛОГа, предназначенными для работы с такими базами данных.
Общие сведения
Материал настоящей работы посвящён освоению принципиально иных
приёмов создания логических программ по сравнению с теми приёмами про-
граммирования, которые изучены в предыдущих лабораторных работах.
Как было сказано ранее, декларативный принцип, лежащий в основе ло-
гического программирования, наряду со своими достоинствами обладает и
присущими только ему недостатками. Так вычисленные с помощью какого­либо правила значения переменных сохраняются только в пределах этого
же правила и утрачиваются при переходе механизма поиска решения к дру-
гому правилу. Фактически сохранить вычисленные в правиле значения ока-
зывается возможным, только распечатав их с помощью предикатов вывода. Только факты хранят доступные для всей программы фиксированные зна-
чения переменных в течение всего времени работы программы. Однако
факты, заданные при написании программы, не могут измениться в процес­се функционирования программы. Для преодоления этого затруднительного
аспекта логического программирования все версии языка ПРОЛОГ имеют
возможности фиксировать или изменять вычисленные значения во время выполнения программы путём добавления или удаления тех или иных фак­тов.
Поскольку добавляемые и удаляемые факты изменяют базу данных про-
граммы, предикаты, используемые в этих фактах, называют предикатами
динамической базы данных. Но так как многие версии ПРОЛОГа имеют воз-
69
можности взаимодействовать с внешними базами данных, такие динамиче-
ские базы данных называют внутренними базами данных.
В программных продуктах фирмы Borland, языках Турбо-ПРОЛОГ и Visual
Prolog, предикаты динамических баз данных должны быть предварительно
описаны, как и обычные предикаты, но только в специальных разделах. На
Турбо-ПРОЛОГе такие разделы называются database (сокращение понятия
«динамическая база данных»). На языке Visual Prolog эти же разделы назы-
ваются facts (так как добавлять и удалять в логическую программу можно не любые клаузы, а только факты, без правил).
Структура программы на Турбо-ПРОЛОГе с использованием динамиче-
ских баз данных имеет следующий вид:
database
<описание предикатов безымянной динамической базы данных>
database - <Имя_1-й_базы_данных>
<описание предикатов 1-й именованной динамической базы данных> . . .
database - <Имя_n-й_базы_данных>
<описание предикатов n-й именованной динамической базы данных>
predicates
<описание "обычных" предикатов логической программы>
clauses
<клаузы логической программы>
Как видно, программа может иметь только одну безымянную базу дан-
ных, но несколько баз данных, имеющих имена. При этом взаимное распо-
ложение разделов, объявляющих предикаты, может быть произвольным.
Имена динамических баз данных, как и имена предикатов, также могут быть
произвольными. Необходимо лишь, чтобы они начинались со строчной ла-
тинской буквы (db1, d_p2 и т. п.).
Аналогичная структура программы на языке Visual Prolog с использова-
нием динамических баз данных выглядит следующим образом:
70
facts
<описание предикатов безымянной динамической базы данных> facts - <Имя_1-й_базы_данных>
<описание предикатов 1-й именованной динамической базы данных> . . .
facts - <Имя_n-й_базы_данных> <описание предикатов n-й именованной динамической базы данных>
predicates
<описание "обычных" предикатов логической программы>
clauses
<клаузы логической программы>
Наличие нескольких баз данных в одной программе не является синтак-
сически необходимым и определяется пользователем в зависимости от же-
лания распределить различные динамические факты по разным разделам.
Для работы с безымянной динамической базой данных в языках Турбо-
ПРОЛОГ и Visual Prolog имеются пять встроенных предикатов второго по- рядка. Аналогичные предикаты есть и в других ПРОЛОГах. Вот эти встро­енные предикаты:
assert(Факт) добавить Факт в произвольное место безымянной ди-
намической базы данных;
asserta(Факт) добавить Факт в начало безымянной динамической
базы данных;
assertz(Факт) добавить Факт в конец безымянной динамической ба-
зы данных;
retract(Факт) удалить одно вхождение клаузы Факт из безымянной
динамической базы данных;
retractall(Факт) удалить все вхождения клаузы Факт из безымянной
динамической базы данных.
В дополнение к этим предикатам второго порядка имеются два встроен-
ных служебных предиката. Один из них позволяет сохранять накопленные в безымянной динамической базе данных факты в файле с заданным именем:
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]