Усовершенствование процедуры useranswer
Один из недостатков первой версии процедуры useranswer, который проявился в приведенном выше диалоге, состоит в том, что сформированные системой Prolog имена переменных в выводе этой системы кажутся невыразительными. При отображении диалога для пользователя символы типа 7 необходимо заменять некоторыми осмысленными словами.
Еще один, более серьезный недостаток этой версии процедуры useranswer состоит в следующем. Если в дальнейшем процедура useranswer будет применена для достижения аналогичной цели, то пользователю придется повторно вводить все те же сведения, что и раньше. Если этой экспертной системе в процессе формирования в ней рассуждений придется дважды проверить одну и ту же запрашиваемую цель, она будет раздражать пользователя, снова повторяя один и тот же диалог, а не используя информацию, предоставленную пользователем ранее.
Устраним эти два недостатка. Прежде всего, для усовершенствования внешнего вида запросов к пользователю можно предусмотреть некоторый стандартный формат для каждой запрашиваемой цели. Для этого достаточно добавить в отношение askable второй параметр, обозначающий этот формат, как показано в следующем примере: askable X eats Y, ' ' eats 'Something').
Затем при выдаче пользователю запроса каждую переменную в этом запросе необходимо заменить ключевыми словами в указанном формате, например, следующим образом:
?- useranswer ( X eats Y, [], Answer). Is it true: Animal eats Something? yes.
Animal = peter. Something = meat. Answer = true X = peter Y = meat
В усовершенствованной версии процедуры useranswer, приведенной в листинге 16.4, такое форматирование запросов осуществляется с помощью процедуры format; Goal, ExternFormat, Question, VarsO, Variables)
где Goal - форматируемая цель, a ExternFormat задает внешний формат для Goal, который определен следующим образом: askable ( Goal, ExternFormat)
Далее, Question - это цель Goal, отформатированная в соответствии с форматом ExternFormat. Параметр Variables представляет собой список переменных, которые появляются в цели Goal в сопровождении соответствующих им ключевых слов (как указано в формате ExternFormat), введенных в список VarsO, например, как показано ниже.
Глава 16.Командный интерпретатор экспертной системы
■1,,^ yives 1What' to '«horn',
Question, [], Variables!.
Question - 'Who' gives documents to "Whom1,
Variables = [X/'Who1, Y/'Whom'].
Листинг 16.4. Командный интерпретатор экспертной системы: процедура, которая запрашивает у пользователя необходимую информацию и отвечает на вопросы "why"
% Процедура
!
I useranswer! Goal, Trace, Answer)
%
% формирует с помощью перебора с возвратами решения, связанные с достижением % цели Goal, запрашивая у пользователя информацию.
s Trace - это цепочка родительски:- целей и правил, применяемая для формирования Ч объяснения необходимости затребованной информации
useranswert Goal, Trace, Answer) :-
askable; Goal, _ ) , % Цель, информацию о которой можно запросить
$ у пользователя freshcopy( Goal, Copy!, Переменные в цели Goal переименованы useranswer; Goal, Copy, Trace, Answer, 1) .
% He следует повторно задавать вопрос, касающийся уже конкретизированной цели
useranswer( Goal, _, _, _, N) :-
N > 1, * Вопрос должен Быть задан повторно?
instantiate( Goal),! , % В цели Goal переменные отсутствуют
fail.% Вопрос не следует задавать. повторно
* Можно ли предположить, что цель Goal является истинной или ложной 4 при всех конкреткзациях?
useranswer! Goal, Copy, _, Answer, _) :-wastold ' Copy, Answer, _) , instance_of( Copy, Goal) , !. Предполагаемый ответ Answer для цели Goal
i Выполнить выборку известных решений цели Goal, проиндексированных числами % от N и выше
useranswer,. Goal, _, _, true, H) :-wastcld; Goal, truerM),
:- Есть ли уже вся необходимая информация о цели Goal?
useranswerf Goal, Copy, _, Answer, _) :-end_answers : Copy} , ins-ance_c£ { Copy, Goal}, !, ;- Вся необходимая информация
ч о цели Goal уже имеется fail.
% Запросить у пользователя (дополнительные) решения
useranswerf Goal, _, Trace, Answer, к} :-
askuserf Goal, Trace, Answer, N) . askusert Goal, Trace, Answer, K) :-
askable( Goal, ExternFormat),
format ( Goal, ExternFormat, Question, Variables), .. Определить формат
% вопроса
ask I Goal, Question, Variables, Trace, Answer, H) .
ask( Goal, Question, Variables, Trace, Answer, H) :-
nl,
(Variables - [] , !,
wll'.<- ' 'Is it truer1) % Вывести запрос с приглашением
Часть II. Применение языка Prolog в области искусственного интеллекта
write( 'Any (more) solution to:') % Вывести запрос с приглашением
) ,
write ( Question) , write('? ' ) ,
getreply( Reply), !, % Ответ равен yes/no/why
process! Reply, Goal, Question, Variables, Trace, Answer, N) .
process( why, Goal, Question, Variables, Trace, Answer, N) :-showtrace ( Trace) , ask( Goal, Question, Variables, Trace, Answer, N) .
process) yes, Goal, _, Variables, Trace, true, N) :-
nextindex ( Next) , % Получить новый незанятый индекс для факта wastold
Nextl is Next + 1,
(askvars '.Variables) ,
assertz( wastold( Goal, true. Next)) % Внести решение в базу данных
freshcopyf Goal, Copy) , % Копия цели Goal
useranswer ( Goal, Copy, Trace, Answer, Nextl) % Есть еще ответы? ) .
process! no, Goal, _, _, _, false, N) :-freshcopy( Goal, Copy) ,
wastold ( Copy, true, _) , ! , % 'no' означает, что решений больше нет
assertzf end answers( Goal)), % Отметить конец списка решений
fail
nextindex ( Next) , % Следующий незанятый индекс для факта wastold
assertz { wastold( Goal, false, Next)) . % 'no' означает, что решений больше нет
format' Var, Name, Name, Vars, [Var/Name | Vars]) :-var ( Var) , ! .
format(Atom, Name, Atom, Vars, Vars) :-atomic( Atom) , !, atomic(Name).
format( Goal, Form, Question, VarsO, Vars) :-Goal =.. [Functor | Argsl], Form =.. [Functor | Forms],
formatall ( Argsl, Forms, Args2 , VarsO, Vars), Question =.. [Functor | Args2] .
formatall ( [] , [] , [] , Vars, Vars) .
formatall ( [X | XL], [F | FL], [Q | QL], VarsO, Vars) :-formatall( XL, FL, QL, VarsO, Varsl), formatt X, F, Q, Varsl, Vars).
askvars( [ ] ) .
askvars( [Variable/Name | Variables]) :-
nl, write ( Name) , write(' = '),
read( Variable) ,
askvars Variables) . showtrace( [ ] ) : -
nl, write ( 'This was your question'), nl. showtrace( [Goal by Rule | Trace]) :-
nl, write ( 'To investigate, by ' ) ,
write ( Rule) , write ( ' , ' ) ,
write ( Goal) ,
showtrace( Trace) . instantiate) Term) :-
nurabervars ( Term, 0, 0). % В терме Term переменные отсутствуют
Глава 16.Командный интерпретатор экспертной системы 373
instance of( Tl, T2>: T2 - экземпляр Tl; это означает, что терм Т1 является более обиим или столь же общим, как и Т2
instance of[ Terra, Terml) :- i Экземпляром терма Term является lentil
freshcopyt Terml, Term2), l Копия терка Terml с обновленным множеством
% переменных numbervars! Term2, 0, _), !,
Term = Term2. % Эта цель достигается успешно, если
I Terml - экземпляр терма Term freshcopyt Term, FreshTerm) :- % Создать копию терма Term с пе
% Создать копию терма Term с переименованными переменными
asserts copyt Term)),
retract ( copy! FreshTerm)}, !.
nextindext Next) :- Следующий незанятый индекс тя факта wastold
retract! lastindex( Last)), !, Next is Last + 1,
assert; lastindexf Next)),
% Инициализировать динамические процедуры lastindex/l, wastold/3, end_answers/i
:- assertz { lastindex(O)},
assertz! wastold ( dummy, false, 0)), assertz( end answers! dummy)).
Еще одно усовершенствование, которое позволило бы избежать повторных вопросов к пользователю, реализовать немного труднее. Прежде всего, для этого необходимо запоминать все ответы пользователя, чтобы можно было осуществить их выборку в некоторый последующий момент.Это можно обеспечить, внося в базу данных ответы пользователя как элементы некоторого отношения, например, следующим образом: assert( wastold! шагу gives documents to friends, true) ).
Но в той ситуации, когда пользователь предоставил несколько решений, относя
щихся к одной и той же цели, в базу данных будет внесено несколько фактов, ка
сающихся этой цели. В этом и состоит сложность. Предположим, что в разных мес
тах в базе данных присутствует несколько вариантов одной и той же цели (причем
цель одна, а переменные в ней разные), например, как показано ниже.
(X has у} and % Первый экземпляр факта - Goall
Ш has У1) and % Второй экземпляр факта - Goal2
Кроме того, предположим, что пользователь запросил (с помощью перебора с возвратами) несколько решений для цели Goall. После этого процесс формирования рассуждений перешел к цели С-оа12. А поскольку уже имеются некоторые решения для Goall, то хотелось бы, чтобы система применяла их автоматически также и к цели Goai2 (поскольку они, безусловно, позволяют достичь и Goal2). Теперь предположим, что система пытается применить эти решения для Goal2, ко ни одно из них не позволяет больше достичь какой-либо иной цели. Поэтому система должна возвратиться к GoaI2 и потребовать от пользователя дополнительные решения. Если пользователь предоставит дополнительные решения, их также нужно запомнить. В том случае, если система в дальнейшем вернется к цели Goall, то эти новые решения необходимо также автоматически применить к цели Goall.
Для того чтобы можно было правильно использовать информацию, предоставленную пользователем на разных этапах работы с программой, необходимо проиндексировать эту информацию. Поэтому внесенные в базу данных факты будут иметь следующую форму:
wastold! Goal, Truthvalue, Index)
где Index — счетчик ответов, предоставленных пользователем. Процедура useranswer! Goal/ Trace, Answer)
374 Часть II.Применение языка Prolog в области искусственного интеллекта
должна следить за количеством решений, которые уже были выработаны с помощью перебора с возвратами. Такую задачу можно решить с использованием еще одной процедуры, useranswer, с четырьмя параметрами: useranswer( Goal, Trace, Answer, N)
где N - целое число. В вызове этой процедуры необходимо вырабатывать решения для цели Goal, проиндексированные целым числом N или большим числом. А вызов useranswer ( Goal, Trace, Answer)
предназначен для выработки всех решений для цели Goal. Решения индексируются целыми числами, начиная с 1, поэтому имеет место следующее отношение: useranswer ( Goal, Trace, Answer) :-useranswer ( Goal, Trace, Answer, 1).
Общая схема функционирования процедуры useranswer ( Goal, Trace, Answer, N)
состоит в том, что необходимо формировать решения для цели Goal, вначале осуществляя выборку известных решений, проиндексированных числами от N и больше. После того как известные решения будут исчерпаны, нужно начать выдавать пользователю запросы, касающиеся цели Goal, и вносить в базу данных полученные таким образом новые решения, проиндексированные должным образом последовательно возрастающими числами. После того как пользователь сообщит, что решений больше нет, внести в базу данных следующий факт: end_answers( Goal)
А если пользователь сразу же сообщит, что решений вообще не существует, то непосредственно внести в базу данных такой факт: wastoldf Goal, false, Index)
При выборке решений процедура useranswer должна правильно интерпретировать такую информацию.
Но есть еще одна сложность. Пользователь имеет также право задавать общие решения, оставляя некоторые переменные неконкретизированными. А если есть возможность осуществить выборку положительного решения, более общего или столь же общего, как Goal, то, безусловно, нет смысла продолжать задавать вопросы, касающиеся Goal, если уже известно самое общее решение. Если же является истинным следующее высказывание: wastold( Goal, false, _) то должно быть принято аналогичное решение.
Все эти требования учтены в программе useranswer, приведенной в листинге 16.4. Введен еще один параметр, Сору (копия цели Goal), который используется в некоторых согласованиях вместо Goal, чтобы нельзя было уничтожить переменные в цели Goal. В этой программе применяются также два вспомогательных отношения. Одним из них является следующее: instantiated ( Term)
Это отношение принимает истинное значение, если терм Term не содержит переменных. Другим из них является instance_of( Term, Terml)
где Terml — экземпляр терма Term; это означает, что Term является, по меньшей мере, таким же общим, как Terml, например, как показано ниже. instance^of ( X gives information to Y, тагу gives information to Z) В этих двух процедурах используется еще одна процедура:
numbervars( Term, N, м)
Эта процедура "нумерует" переменные в терме Term, заменяя каждую переменную в этом терме некоторым вновь сформированным термом таким образом, чтобы
Глава 16.Командный интерпретатор экспертной системы
эти "пронумерованные" термы соответствовали целым числам между от N до М-1. Например, предположим, что эти термы представлены в следующей форме: var/0, var/l, var/2, . .. В таком случае вопрос
?- Term = f( X, t( a, Y, X) ), numbervars ( Term, 5, Ml.
приведет к получению такого результата: Term = f( var/5, t( a, var/6, var/5)) X = var/5 Y = var/6
M = 7
Подобная процедура numbervars часто предоставляется в системе Prolog в виде встроенного предиката. В противном случае ее можно ввести в программу, как показано ниже.
numbervars ( Term, N, Nplusl) :-
var ( Term), ! , % Переменная?
Term = var/N,
Nplusl is N + 1. numbervars( Term, N, Mi :-
Term =.. [Functor I Args] , % Структура или атом
numberargs { Args, Ы, M) . % Количество переменных в параметрах
numberargs ( [ ] , N, Ы) :- ! . numberargs ( [X I L] , N, M) :-
numbervars( X, Ы, N1),
numberargs( L, N1, M).