Метод ОО можно использовать и иначе, например, для обработки выбираемых элементов. Программа, изображенная на листинге 4.5, демонстрирует использование ОО-метода для создания списка выбранных из базы данных элементов.
Листинг 4.5
_______________________________________________________________
/* Программа: Новые детские имена |
*/ |
|
|
/* Назначение: Демонстрация использования предиката |
*/ |
||
/* |
cut (!) и ОО-метода |
*/ |
|
domains |
|
|
|
|
name = symbol |
|
|
predicates child(name)
go_and_get_them
goal
go_and_get_them
clauses
child("Tom"). child("Alice "). child("Diana "). child("Alice "). child("Beth"). child("Lee "). child("Alice ").
go_and_get_them :-
write(" Список имен"), nl,nl,
child(Name),
Name="Alice", write(" ",Name),nl, fail.
_______________________________________________________
Как и в программе, формирующей список имен детей, в этой программе child(name) является предикатом базы данных, содержащим информацию, необходимую для генерации списка имен. Имеется 7 альтернативных утверждений. Три из них содержат имя Alice. Правило, которое выбирает и выдает имя Alice, имеет вид:
get_alice :-
child(Name),
81
Name="Alice", write(" ", Name),nl, fail.
В базе данных имя Alice встречается трижды, поэтому результатом работы приведенного правила будет список трех одинаковых имен.
Правило get_alice находит все (три) возможные означивания переменной Name, что является результатом применения метода ОПН. Однако при желании можно выдать только первый экземпляр значения переменной Name. Это достигается введением в правило выборки отсечения:
get_first_alice :-
child(Name),
Name="Alice", write(" ", Name), nl, !,
fail.
В результате будет получен список, состоящий из единственного имени Alice. Утверждение цели (правила) в предыдущем примере программы содержит элементы правила get_first_alice. Оно может быть использовано и как внутренняя и как внешняя цель. Тщательно проанализировав это правило, можно заметить, что предикат fail используется только один раз. К моменту, когда он получает управление, предикат cut уже устранил всякую возможность отката, в результате чего предикат fail оказывается бесполезным. Отметим, что методы отката и отсечения здесь представлены в самой общей форме, модифицировать которую для конкретного применения не составит труда. Таким образом, диапазон применения отката и отсечения достатоточно широк.
*Упражнение
4.7.Модифицируйте программу, формирующую список новых имен. Для этого:
a)добавьте предикат, который содержит как первое, так и второе имя некоторых детей. Используйте следующий формат предиката:
child(First_name, last_name).
b)расширьте набор утверждений так, что бы включить первое
ивторое имя для всех детей;
c)напишите правило для выдачи на печать имен детей, второе имя которых Smith (Смит);
d)напишите правило для выдачи на печать полного имени, если первое имя есть Alice;
e)выполните модифицированные программы.
4.4.3 Метод повтора (МП), определяемый пользователем
МП-метод, как и ОО-метод, использует откат. Но в МП-методе выполнить откат возможно всегда в отличии от ОО-метода, где откат выполняются
82
только после искусственного созданного неуспешного результата. Правило рекурсии общего вида имеет более сложную структуру и является обобщением этих методов.
Вид правила повтора, определяемого пользователем, следующий: repeat. /* повторить */
repeat :- repeat.
Первый repeat является утверждением, объявляющим предикат repeat истинным. Первый repeat не создает подцелей, поэтому данное правило всегда успешно. Однако, поскольку имеется еще один вариант для этого правила, то указатель отката устанавливается на первый repeat. Второй repeat - это правило, которое использует само себя как компоненту (третий repeat).Второй repeat вызывает третий repeat, и этот вызов вычисляется успешно, так как первый repeat удовлетворяет подцели repeat. Следовательно, правило repeat так же всегда успешно. Предикат repeat будет вычисляться успешно при каждой новой попытке его вызвать после отката. Факт в правиле будет использоваться для выполнения всех подцелей программы. Таким образом, repeat это рекурсивное правило, которое никогда не бывает неудачным.
Правило repeat широко используется в качестве компоненты других правил. Примером этого может служить программа Эхо (листинг 4.6), которая считывает строку, введенную с клавиатуры, и дублирует ее на экран. Если пользователь введет stop, то программа завершается.
Листинг 4.6
_______________________________________________________________
/* Программа: Эхо |
*/ |
|
/* Назначение: Демонстрация использования МП-метода, |
*/ |
|
/* |
определенного пользователем |
*/ |
predicates write_message repeat do_echo check(name)
goal
write_message, do_echo.
clauses repeat.
repeat :- repeat.
write_message :-
nl, write("Введите, пожалуйста, имена"), nl,
83
write("Я повторю их"), nl,
write("Чтобы остановить меня, введите stop"),nl,nl.
do_echo :-
repeat,
readln(Name),
write(Name),nl,
check(Name),!.
check(stop) :-
nl, write(" - OK, bye!"). check(_) :- fail.
_______________________________________________________
Правило repeat является первым в разделе утверждений программы Эхо. Второе правило выводит информацию для пользователя. Третье правило do_echo (выполнить_эхо) является правилом повтора, определенным пользователем. Его первая компонента есть repeat:
do_echo :- repeat,
readln(Name),
write(Name),nl,
check(Name),!.
Утверждение repeat вызывает повторное выполнение всех следующих за ним компонент. Предикат readnl(Name) считывает строку с клавиатуры, write(Name) выдает (или моделирует эхо) ее на экран.
Последнее подправило check(Name) имеет два возможных значения. Одно определяется подправилом:
check(stop) :-
nl, write(" - OK, bye!").
Если вводимая пользователем строка имеет значение stop, то правило будет успешным. При этом курсор сдвигается на начало следующей строки, на экране появляется сообщение "OK, bye!" (до свидания), и процесс повторения завершается. Обратите внимание на символ отсечения (!). Он служит для прекращения откатов, если условие check выполнено. Другое значение check(Name) определяется подправилом:
check(Name) :- fail.
Если значение строки отлично от stop, то результат выполнения этого правила будет fail. В этом случае произойдет откат к правилу repeat. Таким образом, do_echo является конечным правилом в цепи повторений, условие выхода из которой определяется предикатом check. Благодаря тому, что правило repeat является компонентой, правило do_echo становится конечным правилом повтора.
В программе Эхо правило повтора является первой компонентой правила do_echo. Это очень гибкое средство программирования. Ниже будут
84
приведены сведения о других способах его применения. Правило повтора, являясь одной из компонент правила, обеспечивает циклическое выполнение основных функций данного правила.
Подобным образом в МП-правиле можно использовать более одного правила повтора. Ниже приведено МП-правило, включающие два правила повтора:
do_two_things :-
repeat1, <повторяемое тело>, <условие выхода>,!, repeat2, <повторяемое тело>, <условие выхода>,!.
repeat1.
repeat1 :- repeat1.
repeat2.
repeat2 :- repeat2.
<правило для условия выхода 1>. <правило для условия выхода 2>.
Во время определения правил повтора можно вместо имени repeat использовать какое-нибудь другое. Ниже в качестве примеров приведены несколько альтернатив слову repeat:
loop. |
/* цикл */ |
loop :- loop. |
|
loop1. |
|
loop1 :- loop1. |
|
loop2. |
|
loop2 :- loop2. |
|
iterate. |
/* итерация */ |
iterate :- iterate. |
|
recurse. |
/* рекурсия */ |
recurse :- recurse.
МП-метод наиболее эффективен при реализации доступа к данным в базе данных и файлах на диске, а также для организации выдачи на экран и формирования меню. Все эти вопросы будут рассмотрены в следующих главах.
* Упражнения
85