В прологе механизм вывода уже существует, иногда бывает необходимо либо более рационально использовать его либо заставить делать вообще не свойственные ему операции. Для этого используют встроенные предикаты и некоторые типовые приемы.
(см. книгу И.Н. Соломон “Программирование на прологе”)
1. ОПН
Пусть есть БД
giv (“обезъяна”)
………………..
giv (“зебра”)
Необходимо заставить пролог распечатать всех животных
giv(x)
Тот же эффект можно получить используя предикат fail
giv (“обезъяна”), write (“обезъяна”), fail
…………………………………………..
giv (“обезъяна”)
………………..
giv (“зебра”)
……………
run: giv(x), write(x), fail
run.
Если такой предикат будет использован в качестве подцели, она не подставится успешно, если нужен успех то добавим клаузу.
domains
file = datafile (*указатель на файл*)
predicates
giv (symbol, symbol)
run
goal
openwrite (datafile, “Млекопитающие”)
run
closefail(datafile)
clauses
giv (“обезъяна”, “млекопитающие”)
………………………………………
giv (“лебедь”, “птица”)
run: giv(X, “млекопитающие”)
write(X), nl
writedevice(datafile)
write(X), nl
writedevice(screen)
fail
run
2. Отсечение, откат и т.д.
Отсечение используется в 2-х случаях:
1. Если вывод дошел до места то лучше искать не надо, надо улучшить найденные.
2. Если вывод дошел до места, следует прекратить всякие попытки дальнейшего поиска решения.
Пример:
Пусть необходимо ограничить перечень услуг представляемых библиотекой для нерадивого читателя.
услуги (читатель, вид_услуг);
книга_не_возвращена (читатель, книга),!,
минимальные_услуги (вид_услуг)
Примечание:
Не использовать отсечение, а использовать другой прием.
Первые две клаузы можно записывать
услуги (читатель, вид_услуг): - книга_не_возвращена (читатель, книга),
минимальные_услуги (вид_услуг),
услуги(читатель, вид_услуг): - not (книга_не_возвращена (читатель, книга)),
все_услуги (вид_услуг),
2) Когда необходимо вообще прекратить поиск. Пусть необходимо нерадивому читателю полностью отказать в каких-либо услугах
услуги (Чит, Вид_услуг):- кн_не_возвр(Чит, кн), !, fail
услуги (Чит, Вид_услуг):- все_усл(Вид_усл)
Первая клауза рассчитана на отказ в услугах, отсечение не даёт её пересмотреть.
Вторая клауза рассчитана на законопослушного читателя.
Примечание: можно ли их менять местами? (с/м)
Пользоваться отсечением надо тогда, когда мы абсолютно уверены в его необходимости.
Отсечение повышает эффективность работы за счёт узкой специализации. Если могут возникнуть совершенно неожиданные запросы, отсечение может дать побочные эффекты.
Пример: простая программа для определения количества родителей
Число родит(Адам, 0) : – !.
Число родит(Ева, 0) : – !.
Число родит(Х, 2).
Число родит(Ева, Х)?
Х=0
Число родит(Вася, Х)?
Х=2
Система рассчитана на запрос о том, сколько родителей у конкретного индивидуума, если задать вопрос задать по-другому (правда ли, что у Евы 2-е родителей?)
Число родит(Ева, 2)?
Да
Число родит(Адам, N) : – !, N=0.
Число родит(Ева, N) : – !, N=0.
Число родит(Х, 2).
3) Метод повтора, определённый пользователем.
Рассмотрим следующую структуру
Repeat1 является утверждением без аргументов и всегда сопоставляется успешно. Repeat2 является правилом с одной подцелью, ссылающееся на Repeat1.
Если в каком-либо запросе в качестве подцели будет содержаться какая-нибудь Repeat4, то такая подцель будет сопоставляться успешно сколько угодно раз. И каждый раз Prolog будет считать, что он нашел новую альтернативу, т.е. эта конструкция обманывает интерпретатор, порождая альтернативу там, где их на самом деле нет.
a : – b, c, repeat, l, m, …
Пусть среди подцели правее repeat есть приводящие к неуспеху. Подцели пересопоставляются справа налево. Как только ближайшей правой подцелью станет repeat будет полностью повторяться поиск сопоставления по l и m. Это используется для организации циклов.
Do_loop : – repeat,
<тело цикла>
fail
Цикл бесконечный. Для нормальной работы надо менять параметр цикла и предусмотреть условие выхода.
Do_loop : – repeat
<тело>
<усл_вых>
Условие выхода – успех.
Пример:
predicates
write_mess
rep.
echo.
test(symbol)
goal.
write_mess, echo
clouses
rep.
rep : – rep.
write_mes : – nl, write(“Введите строку”), nl,
write(“Я её повторю”), nl,
write(“Для остального - stop”), nl.
echo : – rep,
readln (str),
write (str),
test (str), !.
test (stop) : – nl, write (“Конец работы”).
Test (_): – fail.
При вводе любой строки кроме “stop” предикат test не сопоставляется, тело цикла повторяется с разными строками. Если будет введена строка “stop”, тест сопоставляется успешно. Поводов для отката не видно, да и отсечение в любом случае воспрепятствует откату. Цикл закончен.
Вопрос: так ли необходим предикат отсечения, нельзя ли его убрать?
Примечание: возможно использование нескольких предикатов типа rep.
rep1.
rep1 : – rep1.
rep2.
rep2 : – rep2.
4) Простая рекурсия.
recursive_rule : –
<предикаты>,
recursive_rule
При каждом новом вызове вводятся новые конкретные переменные. Если рекурсивное правило не генерирует указатели отката и последняя подцель – само рекурсивное правило, Turbo Prolog сам убирает все дополнительные операции со стеком, вызванные рекурсией – это называется устранением хвостовой рекурсии.
Восходящая рекурсия – вызов правила с возрастающим аргументом.
Нисходящая – с убывающим.
Очевидно, что среди признаков должен быть хотя бы один, который при неуспехе прекращает рекурсию.
Чтобы прекратить рекурсию среди предикатов.
Пример:
Read_char : – read_char (Ch),
Ch <> ‘$’,
Write (Ch),
Read_char.
Повтор или вывод введённых символов будет до тех пор, пока не будет введён знак $. |