Назад | Содержание| Вперёд 14. 5. Реализация Теперь мы приступим ...

Назад | Содержание| Вперёд

14. 5.    Реализация

Теперь мы приступим к реализации нашейоболочки, следуя тем идеям, которые обсуждались впредыдущем разделе. На рис. 14.9 показаны основныеобъекты, которыми манипулирует оболочка. Цель- это вопрос, подлежащий рассмотрению; Трасса- это цепочка, составленная из"целей-предков" и правил, находящихся междувершиной Цель и вопросом самоговерхнего уровня; Ответ - решающее деревотипа И / ИЛИ для вершины Цель.

Рис. 14. 9.  Отношение рассмотреть(Цель,  Трасса,  Ответ).

Ответ - это И / ИЛИ решающее дерево дляцелевого утверждения Цель.

Основными процедурами оболочки будут:

        рассмотреть( Цель,Трасса, Ответ)

Эта процедура находит ответ Ответ навопрос Цель. Процедура

        ответпольз( Цель,Трасса, Ответ)

порождает решения для тех вопросов Цель,которые можно задавать пользователю. Онаспрашивает пользователя об истинностиутверждения Цель, а также отвечает навопросы "почему". Процедура

        выдать( Ответ)

выводит результат и отвечает на вопросы"как". Все эти процедуры приводятся вдействие процедурой-драйвером эксперт.

14. 5. 1.    Процедура    рассмотреть

Центральной процедурой оболочки являетсяпроцедура

        рассмотреть( Цель,Трасса, Ответ)

которая будет находить ответ Ответ назаданный вопрос Цель, используяпринципы, намеченные в общих чертах в разд. 14.4.1:найти Цель среди фактов базы знаний, илиприменить правило из базы знаний, или спроситьпользователя, или же обработать Целькак И / ИЛИ-комбинацию подцелей.

Аргументы имеют следующий смысл и следующуюструктуру:

Цель             вопрос,подлежащий рассмотрению, представленный

                      как И / ИЛИ-комбинация простых утверждений,например

                      X имеет перья или X летает или

                      Xоткладывает яйца

Трасса        цепочка,составленная из целей-предков и правил,

                   расположенных между Цель и исходнойцелью самого верхнего уровня. Представляется каксписок, состоящий из элементов вида

                      Цель  по  Прав

что означает:  Цель рассматриваласьс использованием правила  Прав.Например, пусть исходной целью будет "питерэто тигр", а текущей целью - "питер естмясо". В соответствии с базой знаний рис. 14.5имеем трассу

                     [( питер это хищник) поправ3,

                       ( питер это тигр) поправ5 ]

Смысл ее можно выразить так:

Я могу использовать "питер ест мясо" длятого, чтобы проверить по прав3, что "питер этохищник".

Далее, я могу использовать "питер этохищник" для того, чтобы проверить по прав5, что"питер это тигр".

Ответ        решающееИ / ИЛИ-дерево для вопроса  Цель. Общаяформа

                  представления для объекта Ответ:

                      Заключение былоНайдено

где Найдено - это обоснование длярезультата Заключение. Следующие трипримера иллюстрируют различные вариантыответов:

(1)        ( соед( радиатор,предохр1) это правда) было

                                                       'найдено как факт'

(2)        (питер ест мясо) этоложь было сказано

(3)        (питер это хищник)это правда было

                   ( 'выведено по' прав3 из

            (питер этомлекопитающее) это правда было

                   ( 'выведено по' прав1 из

            (питер имеетшерсть) это правда было сказано)

            и

            (питер естмясо) это правда было сказано )

На рис. 14.10 показана прологовская программа дляпроцедуры рассмотреть. В этой программереализованы принципы разд. 14.4.1 с использованиемтолько что описанных структур данных.

% Процедура

%

% рассмотреть( Цель, Трасса, Ответ)

%

% находит Ответ на вопрос Цель. Трасса - этоцепочка

% целей-предков и правил. "рассмотреть"стремится найти

% положительный ответ на вопрос. Ответ "ложь"выдается

% только в том случае, когда рассмотрены всевозможности,

% и все они дали результат "ложь".

        :-ор( 900, xfx, :).

        :-ор( 800, xfx, было).

        :-ор( 870, fx, если).

        :-ор( 880, xfx, то).

        :-ор( 550, xfy, или).

        :-ор( 540, xfy, и).

        :- ор( 300, fx, 'выведено по').

        :- ор( 600, xfx, из).

        :- ор( 600, xfx, по).

% В программе предполагается,что ор( 700, хfх,это), ор( 500, fx, не)

        рассмотреть( Цель,Трасса, Цель это правда

                                               было 'найдено как факт') :-

               факт : Цель.

% Предполагается, что для каждого типа цели

% существует только одно правило

        рассмотреть( Цель,Трасса,

               Цель это ПравдаЛожь

               было 'выведено по' Прав из Ответ) :-

            Прав : еслиУсловие то Цель,

                                   % Правило, относящееся к цели

            рассмотреть(Условие, [Цель по Прав | Трасса], Ответ),

            истинность(Ответ, ПравдаЛожь).

        рассмотреть( Цель1 иЦель2, Трасса, Ответ) :-  !,

               рассмотреть( Цель1, Трасса, Ответ1),

               продолжить( Ответ1, Цель1 и Цель2, Трасса, Ответ).

        рассмотреть( Цель1или Цель2, Трасса, Ответ) :-

               рассм_да( Цель1, Трасса, Ответ);

                                   % Положительный ответ на Цель1

               рассм_да( Цель2, Трасса, Ответ).

                                   % Положительный ответ на Цель2

        рассмотреть( Цель1или Цель2, Трасса,

                                                               Ответ1 и Ответ2) :-  !,

               not рассм_да( Цель1, Трасса, _ ),

               not рассм_да( Цель2, Трасса, _ ),

                                   % Нет положительного ответа

               рассмотреть( Цель1, Трасса, Ответ1),

                                   % Ответ1 отрицательный

               рассмотреть( Цель2, Трасса, Ответ2).

                                   % Ответ2 отрицательный

        рассмотреть( Цель,Трасса,

                                           Цель это Ответ было сказано) :-

               ответпольз( Цель, Трасса, Ответ).       % Ответ дан пользователем

        рассм_да( Цель,Трасса, Ответ) :-

               рассмотреть( Цель, Трасса, Ответ),

               положительный( Ответ).

        продолжить( Ответ1,Цель1 и Цель2, Трасса, Ответ) :-

               положительный( Ответ1),

               рассмотреть( Цель2, Трасса, Ответ2),

               ( положительный( Ответ2), Ответ = Ответ1 и Ответ2;

               отрицательный( Ответ2), Ответ = Ответ2).

        продолжить( Ответ1,Цель1 и Цель2, _, Ответ1) :-

               отрицательный( Ответ1).

        истинность( Вопросэто ПравдаЛожь было Найдено,

                                                                           ПравдаЛожь) :-  !.

        истинность( Ответ1 иОтвет2, ПравдаЛожь) :-

               истинность( Ответ1, правда),

               истинность( Ответ2, правда),  !,

               ПравдаЛожь = правда;

               ПравдаЛожь = ложь.

        положительный(Ответ) :-

               истинность( Ответ, правда).

        отрицательный(Ответ) :-

               истинность( Ответ, ложь).

Рис. 14. 10.  Основнаяпроцедура оболочки экспертной системы.

14. 5. 2.    Процедура    ответпольз

Прежде чем перейти к написанию процедуры ответпольз,давайте рассмотрим одну полезнуювспомогательную процедуру

        принять( Ответ)

В процессе диалога часто возникает ситуация,когда от пользователя ожидается ответ "да","нет" или "почему". Процедура принятьпредназначена для того, чтобы извлечь один изэтих ответов, понимая его правильно и в техслучаях, когда пользователь применяетсокращения ('д' или 'н') или делает ошибки. Еслиответ пользователя непонятен, то принятьпросит дать другой вариант ответа.

        принять( Ответ) :-

               read( Ответ1),

               означает( Ответ1, Значение),  !,

                                                           % Ответ1 означает что-нибудь?

               Ответ = Значение;        % Да

               nl, write( 'Непонятно, попробуйте еще раз,     % Нет

                                                       пожалуйста'),   nl,

               принять( Ответ).          % Новая попытка

        означает( да, да).

        означает( д, да).

        означает( нет, нет).

        означает( н, нет).

        означает( почему, почему).

        означает( п, почему).

Следует заметить, что процедурой принятьнужно пользоваться с осторожностью, так как онасодержит взаимодействие с пользователем.Следующий фрагмент программы может служитьпримером неудачной попытки запрограммироватьинтерпретацию ответов пользователя:

        принять( да),интерп_да( ...);

        принять( нет), интерп_нет(...);

        . . .

Здесь, если пользователь ответит "нет", топрограмма попросит его повторить свой ответ.Поэтому более правильный способ такой:

        принять( Ответ),

        ( Ответ = да, интерп_да( ...);

          Ответ = нет,интерп_нет( ...);

          ... )

        Процедура

        ответпольз( Цель,Трасса, Ответ)

спрашивает пользователя об истинностиутверждения Цель. Ответ - эторезультат запроса. Трасса используетсядля объяснения в случае, если пользовательспросит "почему".

Сначала процедура ответпольз должнапроверить, является ли Цельинформацией, которую можно запрашивать упользователя. Это свойство объекта Цельзадается отношением

        можно_спросить(Цель)

которое в дальнейшем будет усовершенствовано.Если спросить можно, то утверждение Цельвыдается пользователю, который, в свою очередь,указывает истинно оно или ложно. Еслипользователь спросит "почему", то емувыдается Трасса. Если утверждение Цельистинно, то пользователь укажет также значениясодержащихся в нем переменных (если таковыеимеются).

Все вышеизложенное можно запрограммировать (вкачестве первой попытки) следующим образом:

        остветпольз( Цель,Трасса, Ответ) :-

               можно_спросить( Цель),           % Можно ли спрашивать

               спросить( Цель, Трасса, Ответ).

                           % Задать вопрос относительно утвержденияЦель

        спросить( Цель,Трасса, Ответ) :-

               показать( Цель),

                           % Показать пользователю вопрос

               принять(Ответ1),                       %Прочесть ответ

               обработать( Ответ1, Цель, Трасса, Ответ).

                           % Обработать ответ

        обработать( почему,Цель, Трасса, Ответ) :-

                           % Задан вопрос "почему"

               показать_трассу( Трасса),

                           % Выдача ответа на вопрос "почему"

               спросить( Цель, Трасса, Ответ).

                           % Еще раз спросить

        обработать( да,Цель, Трасса, Ответ) :-

                           % Пользователь ответил, что Цель истинна

               Ответ = правда,

               запрос_перем( Цель);

                           % Вопрос о значении переменных

               спросить( Цель, Трасса, Ответ).

                           % Потребовать от пользователя новых решений

        обработать( нет,Цель, Трасса, ложь).

                           % Пользователь ответил, что Цель ложна

        показать( Цель) :-

               nl, write( 'Это правда:'),

               write( Цель), write( ?), nl.

Обращение к процедуре запрос_перем( Цель)нужно для того, чтобы попросить пользователяуказать значение каждой из переменных,содержащихся в утверждении Цель:

        запрос_перем( Терм):-

               var( Терм),  !,                % Переменная ?

               nl, write( Терм), write( '='),

               read( Терм).                  % Считать значение переменной

        запрос_перем( Терм):-

               Терм =.. [Функтор | Аргументы],

                               % Получить аргументы структуры

        запрос_арг(Аргументы).

                               % Запросить значения переменных в аргументах

        запрос_арг( [ ]).

        запрос_арг( [Терм |Термы]) :-

               запрос_перем( Терм),

               запрос_арг( Термы).

Проведем несколько экспериментов с процедурой ответпольз.Пусть, например, известно, что пользователя можноспрашивать о наличии бинарного отношения ест:

        можно_спросить( Xест Y).

(В приведенных ниже диалогах междупролог-системой и пользователем текстыпользователя даются полужирным шрифтом,а реплики пролог-системы курсивом).

        ?-  ответпольз(питер ест мясо, [ ], Ответ).

            Этоправда: питер ест мясо?         % Вопроспользователю

            да.                                                       % Ответ пользователя

            Ответ =правда

Более интересный пример диалога (сиспользованием переменных) мог бы выглядетьпримерно так:

        ?-  ответпольз(Кто ест Что, [ ], Ответ).

        Это правда: _17 ест _18?

                               % Пролог дает переменным свои внутренниеимена

        да.

        _17 = питер.

        _18 = мясо.

        Ответ = правда.

        Кто = питер

        Что = мясо;        % Возврат дляполучения других решений

        Это правда: _17 ест _18?

        да.

        _17 = сьюзен.

        _18 = бананы.

        Ответ = правда

        Кто = сьюзен

        Что = бананы;

        Это правда : _17 ест _18?

        нет.

        Ответ = ложь

14. 5. 3.    Усовершенствование процедуры   ответпольз

Один из недостатков нашей процедуры ответпольз,который хорошо виден из приведенного вышедиалога, - это появление на выходе системы имен,генерируемых пролог-системой, что выглядитдовольно неуклюже. Символы, подобные _17,следовало бы заменить на более осмысленныеслова.

Другой, более серьезный дефект этой версиипроцедуры ответпольз состоит вследующем. Если мы еще раз обратимся к ответпольз,задав ту же самую цель, то пользователю придетсяповторно вводить все варианты решений. Поэтому,если наша экспертная система придет в процессерассуждений к рассмотрению той же самой целивторой раз, то, вместо того, чтобы использоватьинформацию, уже полученную от пользователя, онапроведет с пользователем в точности тот же самыйскучный диалог.

Давайте исправим эти два дефекта. Во-первых,улучшение внешнего вида запросов системы будетосновано на введении стандартного формата длякаждой "запрашиваемой" цели. Для этого вотношение можно_спросить мы добавимвторой аргумент, который и будет задавать этотформат, как видно из следующего примера:

        можно_спросить(Хест Y, 'Животное' ест 'Что-то').

При передаче запроса пользователю каждаяпеременная вопроса должна быть заменена наключевое слово, взятое из формата, например:

        ?-  ответпольз( Xест Y, [ ], Ответ).

        Это правда:  Животное ест Что-то?

        да.

        Животное = питер.

        Что-то = мясо.

        Ответ = правда

        X = питер

        Y = мясо

В улучшенной версии процедуры ответпольз,показанной на рис. 14.11, такое форматированиезапросов выполняется процедурой

        формат( Цель,ВнешФормат, Вопрос, Перем0, Перем )

Здесь Цель - утверждение, котороенужно форматировать. ВнешФорматопределяет внешний формат этого утверждения,задаваемый отношением

        можно_спросить(Цель, ВнешФормат)

Вопрос - это Цель,отформатированная в соответствии с ВнешФормат.Перем - список переменных, входящих в Цель,вместе с соответствующими ключевыми словами (какуказано в ВнешФормат), причем список Перемполучается из списка Перем0 добавлениемновых переменных. Например:

        ?-  формат( Xпередает документы Y,

                           'Кто' передает 'Что' 'Кому',

                           Вопрос, [ ], Перем).

        Вопрос = 'Кто'передает документы 'Кому',

        Перем = [ Х/'Кто', Y/'Кому'].

Второе усовершенствование, состоящее вустранении повторных вопросов к пользователю,будет более трудным. Во-первых, все ответыпользователя следует запоминать, с тем чтобы ихможно было отыскать в памяти в более поздниймомент времени. Для этого достаточно сделатьответы пользователя элементами некоторогоотношения и применить assert, например

        assert( сказано( мерипередает документы друзьям, правда) ).

В ситуации, когда имеется несколько решений,предложенных пользователем для одной и той жецели, в память относительно нее будет записанонесколько фактов. Здесь возникает одноосложнение. Допустим, что в нескольких местахпрограммы встречаются различные вариантынекоторой цели (отличающиеся именованиемпеременных). Например:

        ( X имеет Y) и                           % Первый вариант - Цель1

        . . .

        ( X1 имеет Y1) и                       % Второй вариант - Цель2

        . . .

Допустим также, что пользователя просят (черезмеханизм возвратов) предложить несколькорешений для Цель1. Затем процессрассуждений продвигается вплоть до Цель2.Так как у нас уже есть несколько решений для Цель1,мы захотим, чтобы система автоматическиприменила их и к Цель2 (посколькуочевидно, что они удовлетворяют Цель2).Теперь предположим, что система пытаетсяприменить эти решения к Цель2, но ни одноиз них не удовлетворяет некоторой другой цели,расположенной ниже. Система делает возврат к Цель2и просит пользователя предложить новые решения.Если пользователь введет еще несколько решений,то их также придется запомнить. И если система вдальнейшем сделает возврат к Цель1, тоэти новые решения надо будет применить к Цель1.

Для того, чтобы правильным образомиспользовать информацию, вводимую пользователемпо запросам из разных точек программы, мы будемснабжать каждую такую информацию специальныминдексом. Таким образом, факты, запоминаемыесистемой, будут иметь вид

        сказано( Цель,Истинность, Индекс)

где Индекс - это значение счетчика,ответов пользователя. Процедура

        ответпольз( Цель,Трасса, Ответ)

теперь должна будет отслеживать число решений,уже порожденных механизмом возвратов к моментуобращения к этой процедуре. Это можно сделать припомощи другого варианта процедуры ответпользс четырьмя аргументами:

        ответпольз( Цель,Трасса, Ответ, N)

где N - некоторое целое число. Такое обращение к ответпольздолжно порождать решения для Цель синдексами, начиная с N и далее. Обращение

        ответпольз( Цель,Трасса, Ответ)

соответствует получению всех решений,индексируемых, начиная с 1, поэтому мы имеемследующее соотношение:

        ответпольз( Цель,Трасса, Ответ) :-

               ответпольз( Цель, Трасса, Ответ, 1).

Принцип работы процедуры

        ответпольз( Цель,Трасса, Ответ, N)

таков: сначала получить решения для Цель,отыскивая в памяти все уже известные решения синдексами, начиная с N и далее. Когда все старыерешения исчерпаются, начать задавать вопросыпользователю относительно утверждения Цель,записывая полученные таким образом новыерешения в память при помощи assert ииндексируя их должным образом при помощи целыхчисел. Когда пользователь сообщит, что больше нетрешений, записать в память факт

        конец_ответов( Цель)

Если пользователь с самого начала скажет, чторешений нет вообще, то записать факт

        сказано( Цель, ложь,Индекс)

Находя в памяти те или иные решения, процедура ответпольздолжна правильно интерпретировать подобнуюинформацию.

Однако существует еще одна трудность.Пользователь может, оставляя некоторыепеременные неконкретизированными, указыватьобщие решения. Если найдено положительноерешение, более общее, чем Цель, или стольже общее, как Цель, то нет смыслапродолжать задавать вопросы об утверждении Цель,поскольку мы уже имеем более общее решение.Аналогичным образом следует поступить, еслиобнаружен факт

        сказано( Цель, ложь,_ )

Программа ответпольз, показанная нарис. 14.11, учитывает все вышеприведенныесоображения. В нее введен новый аргумент Копия(копия утверждения Цель), которыйиспользуется в нескольких случаях сопоставленийвместо Цель, с тем чтобы оставить внеприкосновенности переменные утверждения Цель.Эта программа использует также двавспомогательных отношения. Одно из них

        конкретный( Терм)

истинно, если Терм не содержитпеременных. Другое

        конкретизация(Терм, Терм1)

означает, что Терм1 есть некотораяконкретизация (частный случай) терма Терм,т. е. Терм - это утверждение не менееобщее, чем Терм1. Например:

        конкретизация( Xпередает информацию Y,

                                   мэри передает информацию Z)

Обе процедуры основаны на еще одной процедуре:

        нумпер( Терм, N, М)

Эта процедура "нумерует" переменные,содержащиеся в Терм, заменяя каждую изних на некоторый специальный новый терм такимобразом, чтобы эти "нумерующие" термысоответствовали числам от N до М-1, Например, пустьэти термы имеют вид

        пер/0, пер/1, пер/2, ...

тогда в результате обращения к системе

        ?-  Терм - f( X, t( a,Y, X)), нумпер( Терм, 5, М).

мы получим

        Терм = f( пер/5, t( а,пер/6, пер/5) )

        М = 7

Отношение, подобное нумпер, частовходит в состав пролог-системы в качествевстроенной процедуры. Если это не так, то егоможно реализовать программно следующимспособом:

        нумпер( Терм, N, Nплюс1):-

               var( Терм),  !,                               % Переменная ?

               Терм = пер/N,

               Nплюс1 is N + 1.

% Процедура

%

% ответпольз( Цель, Трасса, Ответ)

%

% порождает, используя механизм возвратов, всерешения

% для целевого утверждения Цель, которые указалпользователь.

% Трасса - это цепочка целей-предков и правил,

% используемая для объяснения типа "почему".

        ответпольз( Цель,Трасса, Ответ) :-

               можно_спросить( Цель, _ ),               %Можно спросить ?

               копия( Цель, Копия),                         % Переименование переменных

               ответпольз( Цель, Копия, Трасса, Ответ, 1).

% Не спрашивать второй раз относительноконкретизированной цели

        ответпольз( Цель, _,_, _, N) :-

               N > 1,                                                     % Повторный вопрос?

               конкретный( Цель),  !,                       % Больше не спрашивать

               fail.

% Известен ли ответ для всех конкретизацииутверждения Цель?

        ответпольз( Цель,Копия, _, Ответ, _ ) :-

               сказано( Копия, Ответ, _ ),

               конкретизация( Копия, Цель),  !.      % Ответ известен

% Найти все известные решения для Цель синдексами, начиная с N

        ответпольз( Цель, _,_, правда, N) :-

               сказано( Цель, правда, М),

               М >= N.

% Все уже сказано об утверждении Цель?

        ответпольз( Цель,Копия, _, Ответ, _) :-

               конец_ответов( Копия),

               конкретизация( Копия, Цель),  !,     % Уже все сказано

               fail.

% Попросить пользователя дать (еще) решения

        ответпольз( Цель, _,Трасса, Ответ, N) :-

               спросить_польз( Цель, Трасса, Ответ, N).

        спросить_польз(Цель, Трасса, Ответ, N) :-

               можно спросить( Цель, ВнешФормат),

               формат( Цель, ВнешФормат, Вопрос, [ ], Перем),

                                                                    % Получить формат вопроса

               спросить( Цель, Вопрос, Перем, Трасса, Ответ,N).

        спросить( Цель,Вопрос, Перем, Трасса, Ответ, N) :-

               nl,

               ( Перем = [ ],  !,                     % Сформулировать вопрос

               write( 'Это правда: ');

               write( 'Есть (еще) решения для :' )),

               write( Вопрос), write( '?'),

               принять( Ответ1),  !,             %Ответ1 - да/нет/почему

               обработать( Ответ1, Цель, Вопрос, Перем,

                                                                               Трасса, Ответ, N).

        обработать( почему,Цель, Вопрос, Перем,

                                                                               Трасса, Ответ, N):-

                выд_трассу( Трасса),

                спросить( Цель, Вопрос,Перем, Трасса, Ответ, N).

        обработать( да,Цель,_, Перем, Трасса, правда, N) :-

               след_индекс( Инд),

                                       % Получить новый индекс для"сказано"

               Инд1 is Инд + 1,

               ( запрос_перем( Перем),

               assertz( сказано( Цель, правда, Инд) );

                                                                 % Запись решения

               копия( Цель, Копия),           %Копирование цели

               ответпольз( Цель, Копия, Трасса, Ответ, Инд1) ).

                                                                 % Есть еще решения?

        обработать( нет,Цель, _, _, _, ложь, N) :-

               копия( Цель, Копия),

               сказано( Копия, правда, _),  !,

                                       % 'нет' означает, больше нет решений

               assertz( конец_ответов( Цель) ),

                                       % Отметить конец ответов

               fail;

               след_индекс( Инд),

                                       % Следующий свободный индекс для"сказано"

               assertz( сказано( Цель, ложь, Инд) ).

                                       % 'нет' означает нет ни одного решения

        формат( Пер, Имя,Имя, Перем, [Пер/Имя | Перем]) :-

               var( Пер),  !.

        формат( Атом, Имя, Атом,Перем, Перем) :-

               atomic( Атом),  !,

               atomic( Имя).

        формат( Цель, Форм, Вопрос,Перем0, Перем) :-

               Цель =.. [Функтор | Apг1],

               Форм =.. [Функтор | Форм1],

               формвсе( Apг1, Форм1, Арг2, Перем0, Перем),

               Вопрос =.. [Функтор | Арг2].

        формвсе( [ ], [ ], [ ],Перем, Перем).

        формвсе( [Х | СпХ], [Ф |СпФ], [В | СпВ], Перем0, Перем) :-

               формвсе( СпХ, СпФ, СпВ, Перем0, Перем1),

               формат( X, Ф, В, Перем1, Перем).

        запрос_перем( [ ]).

        запрос_перем([Переменная/Имя | Переменные]) :-

               nl, write( Имя), write( '='),

               read( Переменная),

               запрос_перем( Переменные).

        выд_трассу( [ ]) :-

               nl, write( 'Это был ваш вопрос'), nl.

        выд_трассу( [Цель поПрав | Трасса] ) :-

               nl, write( 'Чтобы проверить по' ),

               write( Прав), write( ', что'),

               write( Цель),

               выд_трассу( Трасса).

        конкретный( Терм) :-

               нумпер( Терм, 0, 0).               %Нет переменных в Терм'е

% конкретизация( Т1, Т2) означает, что Т2 -конкретизация Т1,

% т.е. терм Т1 - более общий, чем Т2, или той жестепени

% общности, что и Т2

        конкретизация(Терм, Терм1) :-

                                       % Терм1 - частный случай Терм 'а

               копия( Терм1, Терм2),

                                       % Копия Терм1 с новыми переменными

               нумпер( Терм2, 0, _),  !,

               Терм = Терм2.           % Успех, если Терм1 - частный случайТерм2

        копия( Терм,НовТерм) :-

                                       % Копия Терм' а с новыми переменными

               asserta( copy( Терм) ),

               retract( сору( НовТерм) ),  !.

        посл_индекс( 0).               % Начальный индекс для "сказано"

        след_индекс( Инд) :-        % Следующий индекс для"сказано"

               retract( посл_индекс( ПослИнд) ),  !,

               Инд is ПослИнд + 1,

               assert( посл_индекс( Инд) ).

Рис. 14. 11.  Оболочкаэкспертной системы: Вопросы к пользователю

и ответы на вопросы "почему".

        нумпер( Терм, N, М) :-

               Терм =.. [Функтор | Аргументы],            %Структура или атом

               нумарг( Аргументы, N, M).

                                       % Пронумеровать переменные в аргументах

        нумарг( [ ], N, N) :-  !.

        нумарг( [X | Спис], N, M):-

               нумпер( X, N, N1),

               нумарг( Спис, N1, М).

14. 5. 4.    Процедура     выдать

Процедура

        выдать( Ответ)

приведенная на рис. 14.12, показываетпользователю окончательный результатконсультационного сеанса и дает объяснения типа"как". Ответ включает в себя какответ на вопрос пользователя, так и деревовывода, демонстрирующее как система пришлак такому заключению. Сначала процедура выдатьпредставляет пользователю свое заключение.Затем, если пользователь пожелает узнать, какэто заключение достигнуто, то печатается деревовывода в некоторой удобной для восприятия форме -это и есть объяснение типа "как". Формаобъяснения показана в примере разд. 14.4.3.

14. 5. 5.    Драйвер верхнего уровня

И наконец, для того, чтобы иметь удобный доступк оболочке из интерпретатора Пролога, намнеобходима процедура, выполняющая функцию"драйвера". На рис. 14.13 показано, как могла бывыглядеть предназначенная для этой целипроцедура эксперт. Драйвер экспертпроизводит запуск трех основных модулейоболочки (рис. 14.10 - 14.12) и координирует их работу.Например:

% Выдача заключения консультационногосеанса и

% объяснения типа "как"

        выдать( Ответ) :-

               nl, заключение( Ответ),

               nl, write( 'Хотите узнать, как?'),

               принять( Ответ1),

               ( Ответ1 = да,  !,  отобр( Ответ);

                   true).                                                % Показрешающего дерева

        заключение( Ответ1 иОтвет2) :-  !,

               заключение( Ответ1), write( 'и'),

               заключение( Ответ2).

        заключение(Заключение было Найдено) :-

               write( Заключение).

% "отобр" отображает полное решающеедерево

        отобр( Решение) :-

               nl, отобр( Решение, 0),  !.                          % Отступ 0

        отобр( Ответ1 иОтвет2, Н) :-  !,                    % Отступ Н

               отобр( Ответ1, Н),

               tab( H), write( 'и'), nl,

               отобр( Ответ2, Н).

        отобр( Ответ былНайден, Н) :-                    % Отступ Н

               tab( H), печответ( Ответ),            % Показзаключения

               nl, tab( H),

               write( 'было'),

               отобр1( Найден, Н).                       % Показ доказательства

        отобр1( Выведено изОтвет, Н) :-  !,

               write( Выведено), write( 'из'),       % Показ имени правила

               nl, H1 is H + 4,

               отобр( Ответ, H1).                         % Показ "предшественника"

        отобр1( Найдено, _ ) :-

                                   % Найдено = 'сказано' или 'найдено как факт'

               write( Найдено), nl.

        печответ( Цель этоправда) :-  !,

               write( Цель).           % На выходе 'это правда' опускается

        печответ( Ответ) :-                               %Отрицательный ответ

               write( Ответ).

Рис. 14. 12.  Оболочкаэкспертной системы:

Отображение окончательного результата и

объяснение типа "как".

        ?-  эксперт.

        Пожалуйста,спрашивайте:    % Приглашениепользователю

        X это животное иголиаф это Х.    % Вопроспользователя

        Это правда:  голиафимеет шерсть?

        . . .

14. 5. 6.    Одно замечание по поводупрограммы-оболочки

В некоторых местах нашей программы-оболочкиобнаруживается недостаток той "декларативнойясности", которая так характерна для программ,написанных на Прологе. Причина состоит в том, чтонам пришлось предусмотреть в этой программедовольно жесткое управление процессомфункционирования оболочки. Ведь, согласно нашемузамыслу, экспертная система должна была нетолько находить ответы на вопросы, но и делатьэто некоторым разумным с точки зренияпользователя способом. В связи с этим нампришлось реализовать вполне определенное поведениесистемы в процессе решения задач, а не простонекоторое отношение ввода-вывода. В результатеполучилась программа более процедурногохарактера, чем обычно. Все это может послужитьпримером ситуации, когда, не имея возможностирассчитывать на собственные процедурныемеханизмы Пролога, мы вынуждены взять на себядетальное описание процедурного поведениясистемы.

14. 5. 7.    Цели с отрицанием

Использование знака отрицания в левых частяхправил, а следовательно, и в вопросах,обрабатываемых процедурой рассмотреть,представляется естественным и его следуетразрешить. В качестве первой попытки можнопредложить следующий способ работы с отрицаниемцелей:

% Процедура-драйвер верхнего уровня

        эксперт :-

               принять_вопрос( Вопрос),

                                           % Ввести вопрос пользователя

               ( ответ_да( Вопрос);

                                           % Попытка найти положительный ответ

               ответ_нет( Вопрос) ).

            % Еслинет положительного ответа, то найтиотрицательный

        ответ_да( Вопрос) :-

                                           % Искать положительный ответ на Вопрос

               статус( отрицательный),

                                           % Пока еще нет положительного ответа

               рассмотреть( Вопрос, [ ], Ответ),            % Трассапуста

               положительный( Ответ),           % Искать положительный ответ

               статус( положительный),

                                                          % Найден положительный ответ

               выдать( Ответ), nl,

               write( 'Нужны еще решения?' ),

               принять( Ответ1),         % Прочесть ответ пользователя

               Ответ1 = нет.

                               % В противном случае возврат к"рассмотреть"

        ответ_нет( Вопрос):-

                               % Искать отрицательный ответ на Вопрос

               retract( пока_нет_положительного_решения),  !,

                                                           %Не было положительного решения?

               рассмотреть( Вопрос, [ ], Ответ),

               отрицательный( Ответ),

               выдать( Ответ), nl,

               write( 'Нужны еще решения?' ),

               принять( Ответ1),

               Ответ1 = нет.

                               % В противном случае - возврат к"рассмотреть"

        статус(отрицательный) :-

               assert( пока_нет_положительного_решения).

        статус(положительный) :-

               retract( пока_нет_положительного_решения),  !; true.

        принять_вопрос(Вопрос) :-

               nl, write( 'Пожалуйста, спрашивайте:'), nl,

               read( Вопрос).

Рис. 14. 13.  Оболочкаэкспертной системы: драйвер. Обращение

к оболочке из Пролога при помощи процедуры эксперт.

        рассмотреть( не Цель,Трасса, Ответ) :-  !,

               рассмотреть( Цель, Трасса, Ответ1),

               обратить( Ответ1, Ответ).

                               % Получить обратное истинностное значение

        обратить( Цель этоправда было Найдено,

                         ( не Цель) это ложь было Найдено).

        обратить( Цель этоложь было Найдено,

                         ( не Цель) это правда было Найдено).

Если Цель конкретизирована, то все впорядке, если же нет, то возникают трудности.Рассмотрим, например, такой диалог:

        ?-  эксперт.

        Пожалуйста,спрашивайте:

        не ( X ест мясо).

        Есть (еще) решения для  :  Животное

        да.

        Животное = тигр.

В этот момент система даст ответ:

        не ( тигр ест мясо) этоложь

Такой ответ нас не может удовлетворить.Источник затруднения следует искать в том, какойсмысл мы вкладываем в вопросы типа

        не ( X ест мясо)

В действительности мы хотим спросить:"Существует ли такой X, что X не ест мяса?"Однако процедура рассмотреть (так какмы ее определили) проинтерпретирует этот вопросследующим образом:

    (1)        Существуетли такой X, что X ест мясо?

    (2)        Да, тигр естмясо.

    Итак,

    (3)        не (тигр естмясо) это ложь.

Короче говоря, интерпретация такова - "Правдали, что никакой X не ест мясо?" Положительныйответ мы получим, только если никто не естмяса. Можно также сказать, что процедура рассмотретьотвечает на вопрос так, как будто X находится подзнаком квантора всеобщности:

        для всех X: не (X естмясо)?

а не квантора существования, в чем и состоялонаше намерение:

        для некоторого X: не(X ест мясо)?

Если рассматриваемый вопрос конкретизирован,то проблемы исчезают. В противном случаеправильный способ работы с отрицаниямистановится более сложным. Например, вотнекоторые из возможных правил:

Для того, чтобы рассмотреть (не Цель),рассмотрите Цель, а затем:

если Цель это ложь, то (не Цель) это правда;

если Цель' - это некоторое решение для Цель, и Цель' - утверждение той же степени общности, что и Цель, то (не Цель) это ложь;

если Цель' - это некоторое решение для Цель, и Цель' - более конкретное утверждение, чем Цель, то об утверждении (не Цель) нельзя сказать ничего определенного.

Можно избежать всех этих осложнений, еслипотребовать, чтобы отрицания стояли только передконкретизированными целями. Если правила базызнаний формулировать должным образом, то частоудается удовлетворить этому условию. Нам этоудалось в "правиле поломки" (рис. 14.7):

        правило_поломки:

                           если

                                   вкл( Прибор) и

                                   прибор( Прибор) и               % Конкретизация

                                   не работает( Прибор) и

                                   соед( Прибор, Предохр) и

                                   доказано( цел( Предохр) )

                           то

                                   доказано( неиспр( Прибор) ).

Здесь условие

        прибор( Прибор)

"защищает" следующее за ним условие

        не работает( Прибор)

от неконкретизированной переменной.

Упражнение

14. 3.    База знаний может, впринципе, содержать циклы. Например:

        прав1:  еслибутылка_пуста то джон_пьян.

        прав2:  если джон_пьянто бутылка_пуста.

Работая с подобной базой знаний, наша процедурарассмотреть может зациклиться наобработке одних и тех же целей. Внесите впроцедуру рассмотреть изменения,предотвращающие зацикливание. Используйте дляэтого объект Трасса. Однако соблюдайтеосторожность: если текущая цель сопоставимас одной из предыдущих целей, то такую ситуациюследует рассматривать как цикл только в томслучае, когда текущая цель имеет большую, степеньобщности, чем предыдущая.

Назад | Содержание| Вперёд









Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх