А.1. Краткая история CLIPS
А.1. Краткая история CLIPS
Название языка CLIPS — аббревиатура от С Language Integrated Production System. Язык был разработан в Центре космических исследований NASA (NASA's Johnson Space Center) в середине 1980-х годов и во многом сходен с языками, созданными на базе LISP, в частности OPS5 и ART. Использование С в качестве языка реализации объясняется тем, что компилятор LISP не поддерживается частью распространенных платформ, а также сложностью интеграции LISP-кода в приложения, которые используют отличный от LISP язык программирования. Хотя в то время на рынке уже появились программные средства для задач искусственного интеллекта, разработанные на языке С, специалисты из NASA решили создать такой продукт самостоятельно. Разработанная ими система в настоящее время доступна во всем мире, и нужно сказать, что по своим возможностям она не уступает множеству гораздо более дорогих коммерческих продуктов.
Первая версия представляла собой, по сути, интерпретатор порождающих правил. Процедурный язык и объектно-ориентированное расширение CLIPS Object-Oriented Language (COOL) были включены в этот программный продукт только в 1990-х годах. Существующая в настоящее время версия может эксплуатироваться на платформах UNIX, DOS, Windows и Macintosh. Она является хорошо документированным общедоступным программным продуктом и доступна по сети FTP с множества университетских сайтов. Исходный код программного пакета CLIPS распространяется совершенно свободно и его можно установить на любой платформе, поддерживающей стандартный компилятор языка С. Однако я бы рекомендовал пользоваться официальной версией для определенной платформы, поскольку такие версии оснащены пользовательским интерфейсом, включающим меню команд и встроенный редактор.
Это Приложение организовано следующим образом. В разделе А.2 рассмотрены основные функции языка описания правил и процедурного языка. В разделе А.З представлены методы работы с объектами и показано, как использовать их в сочетании с правилами и процедурами. В разделе А.4 описан пример, демонстрирующий некоторые приемы программирования правил, а в разделе А.5 резюмируются характеристики этого программного продукта и предлагаются темы для более углубленного изучения.
А.2.1. Факты
А.2.1. Факты
Сразу после запуска CLIPS-приложения на выполнение на экране появится приглашение, извещающее пользователя, что он работает с интерпретатором.
CLIPS>
CLIPS> (assert (today is Sunday))
<Fact-0>
CLIPS> (assert (weather is warm))
<Fact-l>
Для вывода списка фактов, имеющихся в базе, используется команда facts:
CLIPS> (facts)
f-0 (today is Sunday)
f-1 (weather is warm)
CLIPS> (retract 1)
CLIPS> (facts)
f-0 (today is Sunday)
CLIPS> (clear) CLIPS> (facts)
(deffacts today
(today is Sunday)
(weather is warm) )
CLIPS> (undeffacts today)
CLIPS> (load "my file")
CLIPS> (reset)
f-0 (initial-fact)
Можно проследить, как выполняется команда reset, если перед выполнением приведенных выше команд установить режим слежения среды разработки. Для этого нужно вызвать команду Watch из меню Execution и установить в ней флажок Facts.
А.2.2. Правила
А.2.2. Правила
В языке CLIPS правила имеют следующий формат:
(defrule <имя правила>
< необязательный комментарий >
< необязательное объявление >
< предпосылка_1 >
< предпосылка_т > =>
< действие_1 >
< предпосылка_п > )
Например:
(defrule chores
"Things to do on Sunday"
(salience 10)
(today is Sunday)
(weather is warm) =>
(assert (wash car))
(assert (chop wood) )
(today is Sunday) (weather is warm)
(wash car) (chop wood)
"Things to do on Sunday"
"Что сделать в воскресенье"
(salience 10)
(defrule fun
"Better things to do on Sunday"
(salience 100)
(today is Sunday)
(weather is warm) =>
(assert (drink beer))
(assert (play guitar)) )
Обычно в определении правила присутствуют и переменные. Если, например, правило
(defrule pick-a-chore
"Allocating chores to days"
(today is ?day)
(chore is ?job) =>
(assert (do ?job on ?day)) )
будет сопоставлено с фактами
(today is Sunday) (chore is carwash)
(do carwash on Sunday).
Аналогично, правило
(defrule drop-a-chore
"Allocating chores to days"
(today is ?day)
?chore <- (do ?job on ?day) =>
(retract ?chore) )
(today is Sunday)
(do carwash on Sunday)
(do carwash on Sunday)
(do carwash on Sunday)
(do ? ? Sunday)
(do ? on ?)
(do ? on ?when)
(do $?)
(do $? Sunday)
(do ?chore $?when)
(on Sunday)
А.2.3. Наблюдение за процессом интерпретации
А.2.3. Наблюдение за процессом интерпретации
Теперь на простом примере познакомимся с возможностями, которые предоставляет среда разработки CLIPS в части отладки программы, состоящей из правил и фактов. Введите в текстовый файл правило, а затем загрузите этот файл в среду CLIPS.
(defrule start
(initial-fact)
(printout t "hello, world" crlf) )
CLIPS> (reset)
Затем запустите интерпретатор. Для этого либо введите эту команду run в командную строку интерпретатора
CLIPS> (run)
В ответ программа должна вывести сообщение hello, world, знакомое всем программистам мира. Для повторного запуска программы повторите команды reset и run.
Если в меню Execution^Watch ранее был установлен флажок Rules или перед запуском программы на выполнение вы ввели в командную строку команду watch rules, то на экране появится результат трассировки процесса выполнения
CLIPS> (run) FIRE 1 start: f-0 hello, world
CLIPS> (dribble-on "dribble.dp")
TRUE
CLIPS> (dribble-off)
TRUE
А.2.4. Использование шаблонов
А.2.4. Использование шаблонов
Для определения фактов можно использовать не только списочные структуры, но и шаблоны, которые напоминают простые записи. (Шаблоны в CLIPS не имеют ничего общего с шаблонами C++.) Шаблон выглядит примерно так:
(deftemplate student "a student record"
(slot name (type STRING)) (slot age (type NUMBER) (default 18))
Если в программу включено приведенное выше определение шаблона, то выражение
(deffacts students
(student (name fred))
(student (name freda) (age 19)) )
(student (name fred) (age 18)) (student (name freda) (age 19))
A.2.5. Определение функций
A.2.5. Определение функций
В языке CLIPS функции конструируются примерно так же, как в языке LISP (см. главу 4). Существенное отличие состоит в том, что переменные должны иметь префикс ?, как это показано в приведенном ниже определении.
(deffunction hypotenuse (?a ?b)
(sqrt (+ ( ?a ?a) ( ?b ?b)) )
(deffunction <имя функции (<аргумент> ... <аргумент>) <выражение>
<выражение> )
Функция возвращает результат последнего выражения в списке. Иногда выполнение функции имеет побочные эффекты, как в приведенном ниже примере.
(deffunction init (?day)
(reset)
(assert (today is ?day)) )
будет выполнена команда reset и, следовательно, очищена база фактов, а затем в нее будет включен новый факт (today is Sunday).
А.2. Правила и функции в CLIPS
А.2. Правила и функции в CLIPS
CLIPS включает в язык представления порождающих правил и язык описания процедур.
В этом разделе мы рассмотрим оба этих модуля, сопровождая описание отдельных функций примерами.
Основными компонентами языка описания правил являются база фактов (fact base) и база правил (rule base). На них возлагаются следующие функции:
база фактов представляет исходное состояние проблемы (см. главу 2);
база правил содержит операторы, которые преобразуют состояние проблемы, приводя его к решению (см. главы 2 и 3).
Машина логического вывода CLIPS сопоставляет эти факты и правила и выясняет, какие из правил можно активизировать. Это выполняется циклически, причем каждый цикл состоит из трех шагов:
(1) сопоставление фактов и правил;
(2) выбор правила, подлежащего активизации;
(3) выполнение действий, предписанных правилом.
Такой трехшаговый циклический процесс иногда называют "циклом распознавание— действие" (см. главу 5).
А.3. Объектно-ориентированные средства в CLIPS
А.3. Объектно-ориентированные средства в CLIPS
Использование объектно-ориентированных средств в CLIPS позволяет значительно упростить программирование правил, поскольку для обновления данных можно применять механизм передачи и обработки сообщений методами классов. В этом разделе мы продемонстрируем, как это делается на примере, который моделирует правила обращения с полуавтоматическим пистолетом.
Первым делом определим класс pistol, в котором будут перечислены свойства, необходимые для моделирования.
(defclass pistol
(is-a USER)
(role concrete)
(pattern-match reactive)
(slot safety (type SYMBOL) (create-accessor read-write))
(slot slide (type SYMBOL) (create-accessor read-write))
(slot hammer (type SYMBOL) (create-accessor read-write))
(slot chamber (type INTEGER) (create-accessor read-write))
(slot magazine (type SYMBOL) (create-accessor read-write))
(slot rounds (type INTEGER) (create-accessor read-write)) )
pistol — это пользовательский класс;
pistol является конкретным классом, т.е. возможно создание экземпляров этого класса (альтернативный тип — абстрактный класс, который играет ту же роль, что и виртуальный класс в C++);
экземпляры класса pistol могут быть использованы в качестве объектов данных, которые можно сопоставлять с условиями в правилах и использовать в действиях, определенных правилами.
Следующие пять слотов представляют свойства и члены данных класса:
слот safety (предохранитель) может содержать символ on или off;
слот slide (затвор) может содержать значение forward или back, т.е. хранит информацию о положении затвора;
слот hammer (курок) содержит информацию о состоянии курка, back или down;
слот chamber (патронник) содержит значение 1 или 0, в зависимости от того, есть ли патрон в патроннике;
слот magazine (обойма) может содержать значение in или out, в зависимости от того, вставлена ли обойма;
слот rounds (патроны) содержит текущее количество патронов в обойме.
Для того чтобы иметь возможность записывать в слот новое значение или считывать текущее, нужно разрешить формирование соответствующих функций доступа через фацет create-accessor. Теперь сформируем экземпляр класса pistol с помощью следующего выражения:
(definstances pistols (РРК of pistol (safety on)
(slide forward) (hammer down) (chamber 0) (magazine out) (rounds 6)
Теперь, имея в программе определение класса и сформировав экземпляр класса, разработаем правила и обработчики сообщений, с помощью которых можно описать отдельные операции обращения с пистолетом и стрельбы из него. Для этого сначала разработаем шаблон задачи. Желательно отслеживать две вещи:
есть ли патрон в патроннике;
произведен ли выстрел.
Для этого можно использовать следующий шаблон:
(deftemplate range-test
(field check (type SYMBOL) (default no))
(field fired (type SYMBOL) (default no)) )
(defrule start
(initial-fact) =>
(assert (range-test)) )
Следующие три правила будут проверять, правильно ли снаряжен пистолет.
(defrule check
(object (name [PPK]) (safety on) (magazine out)
?T <- (range-test (check no)) =>
(send [PPK] clear)
(modify ?T (check yes) )
(defmessage-handler pistol clear ( )
(dynamic-put chamber 0)
(ppinstance) )
В следующих двух правилах обрабатываются ситуации, когда пистолет снаряжен неправильно, — не установлен на предохранитель или в него вставлена обойма. Правило correctl устанавливает пистолет на предохранитель, а правило correct2 извлекает из него обойму.
(defrule correctl
(object (name [PPK]) (safety off) )
(range-test (check no)) =>
(send [PPK] safety on)
)
(defrule correct2
(object (name [PPK]) (safety on) (magazine in))
(range-test (check no)) =>
(send [PPK] drop) )
(defmessage-handler pistol safety (?on-off)
(dynamic-put safety ?on-off)
(if (eq ?on-off on)
then (dynamic-put hammer down)
) )
Обработчик сообщения drop просто извлекает обойму из пистолета.
(defmessage-handler pistol drop ()
(dynamic-put magazine out) )
(defrule mag-in
(object (name [PPK]) (safety on) (magazine out))
(range-test (fired no) (check yes)) =>
(send [PPK] seat) )
(defmessage-handler pistol seat ()
(dynamic-put magazine in) )
(defrule mag-in
?gun <- (object (name [PPK]) (safety on)
(magazine out)) (range-test (fired no) (check yes)) =>
(modify ?gun (magazine in) )
(defrule load
(object (name [PPK]) (magazine in) (chamber 0)) =>
(send [PPK] rack) )
(defmessage-handler pistol rack ()
(if (> (dynamic-get rounds) 0) then (dynamic-put chamber 1)
(dynamic-put rounds (- (dynamic-get rounds) 1))
(dynamic-put slide forward) else (dynamic-put chamber 0)
(dynamic-put slide back)
(defrule ready
(object (name [PPK]) (chamber 1)) =>
(send [PPK] safety off) )
Правило fire выполняет стрельбу.
(defrule fire
(object (name [PPK]) (safety off);
?T <- (range-test (fired no)) =>
(if (eq (send [PPK] fire) TRUE)
then (modify ?T (fired yes))) )
(def message-handler pistol fire () (if (and
(eq (dynamic-get chamber) 1) (eq (dynamic-get safety) off)
)
then (printout t crlf "BANG!" t crlf)
TRUE
else (printout t crlf "click" t crlf) FALSE
)
После завершения стрельбы пистолет нужно вновь вернуть в положение "по-походному". Начинается это с того, что пистолет устанавливается на предохранитель, для чего используется ранее разработанный обработчик сообщения safety.
(defrule unready
(object (name [PPK]) (safety off))
(range-test (fired yes)) =>
(send [PPK] safety on) )
(defrule drop
(object (name [PPK]) (safety on))
(range-test (fired yes)) =>
(send [PPK] drop) )
(defrule unload
(object (name [PPK]) (safety on) (magazine out))
(range-test (fired yes)) =>
(send [PPK] clear) )
А.4.1. Анализ проблемы
А.4.1. Анализ проблемы
Первым этапом любого программного проекта является анализ решаемой проблемы. Эксперт должен уметь решить проблему, а инженер по знаниям должен разобраться, как именно было получено решение. При решении нашей задачи вам придется выступить в обеих ипостасях.
Предложенные головоломки можно решить, систематически анализируя, что случится, если персонаж, произносящий реплику, является правдолюбцем, а что, если он — лжец. Обозначим через Т(А) факт, что А говорит правду и, следовательно, является правдолюбцем, а через F(A) — факт, что А лжет и, следовательно, является лжецом.
Рассмотрим сначала головоломку Р1. Предположим, что А говорит правду. Тогда из его реплики следует, что либо А лжец, либо В правдолюбец. Формально это можно представить в следующем виде:
Т(А) => F(A) v T(B)
Поскольку А не может быть одновременно и лжецом и правдолюбцем, то отсюда следует
T(A)=> Т(В). Аналогично можно записать и другой вариант. Предположим, что А лжет:
F(A) => -{F(A) v T(B)).
Упростим это выражение:
Сравнивая оба варианта, нетрудно прийти к выводу, что только последний правильный, поскольку в первом варианте мы пришли к выводу, противоречащему условиям (не могут быть правдолюбцами одновременно А и В).
Таким образом, рассматриваемая проблема относится к типу таких, решение которых находится в результате анализа выводов, следующих из определенных предположений, и поиска в них противоречий (или отсутствия таковых). Мы предполагаем, что определенный персонаж говорит правду, а затем смотрим, можно ли в этом случае так распределить "роли" остальных персонажей, что не будут нарушены условия, сформулированные в репликах. На жаргоне, принятом в математической логике, предположение о правдивости или лживости множества высказываний называется интерпретацией, а вариант непротиворечивого присвоения значений истинности элементам множества — моделью.
Однако наши головоломки включают и нечто, выходящее за рамки типовых проблем математической логики, поскольку реплики в них может произносить не один персонаж (как в головоломке Р2), а на реплику одного персонажа может последовать ответная реплика другого (как в головоломке РЗ). В исходной версии программы, которую мы рассмотрим ниже, это усложнение отсутствует, но в окончательной оно должно быть учтено. Мы покажем, что постепенное усложнение программы довольно хорошо согласуется с использованием правил.
На практике оказывается, что в первой версии программы удобнее всего воспользоваться "вырожденным" вариантом проблемы, т.е. постараться решить ее в тривиальном виде, который, тем не менее, несет в себе многие особенности реального случая. Вот как это выглядит в отношении наших правдолюбцев и лжецов.
Р0. А заявляет: "Я лжец". Кто же в действительности А — лжец или правдолюбец?
Мы только что фактически процитировали хорошо известный Парадокс Лгуна. Если А лжец, то, значит, он врет, т.е. в действительности он правдолюбец. Но тогда мы приходим к противоречию. Если же А правдолюбец, т.е. говорит правду, то в действительности он лжец, а это опять противоречие. Таким образом, в этой головоломке не существует непротиворечивого варианта "распределения ролей", т.е. не существует модели в том смысле, который придается ей в математической логике.
Есть много достоинств в выборе для прототипа программы варианта головоломки Р0.
В головоломке присутствует только один персонаж.
Выражение не содержит логических связок, таких как И или ИЛИ, или кванторов, вроде квантора общности (все) и прочих.
Отсутствует ответная реплика.
В то же время существенные черты проблемы в этом варианте присутствуют. Мы по-прежнему должны попытаться отыскать непротиворечивую интерпретацию высказывания А, т.е. должны реализовать две задачи, присутствующие в любых вариантах подобной головоломки:
формировать альтернативные интерпретации высказываниям;
анализировать наличие противоречий.
Вы увидите, что для выполнения этих двух задач потребуется использовать механизм, очень близкий к тем, которые мы рассматривали при описании систем обработки правдоподобия в главах 17 и 19.
А.4.2. Онтологический анализ и представление знаний
А.4.2. Онтологический анализ и представление знаний
Следующий этап — определить, с какими видами данных нам придется иметь дело при решении этого класса головоломок. Какие объекты представляют интерес в мире правдолюбцев и лжецов и какими атрибутами эти объекты характеризуются?
По-видимому, для решения задач этого класса нам придется иметь дело со следующими объектами.
Персонажи, произносящие реплики. Произносимая реплика характеризует либо самого персонажа, либо прочих персонажей, либо и тех и других. Персонаж может быть либо правдолюбцем, либо лжецом.
Утверждение, содержащееся в реплике. Это утверждение может быть либо целиком лживым, либо абсолютно правдивым (истинным).
Немного поразмыслив, мы придем к выводу, что существуют еще и другие объекты, которые необходимо учитывать при решении задач этого класса.
Существует среда (мир), которая характеризуется совокупностью наших предположений. Например, существует мир, в котором мы предположили, что А — правдолюбец, а следовательно, высказанное им утверждение (или утверждения) истинно. Это предположение влечет за собой разные следствия, которые образуют контекст данного гипотетического мира.
Существует еще нечто, что мы назовем причинами, или причинными связями (reasons), которые связывают высказывания в том или ином гипотетическом мире. Если А утверждает, что "В — лжец", и мы предполагаем, что А — правдолюбец, то это утверждение является причиной (основанием), по которой мы можем утверждать, что в данном гипотетическом мире В — лжец, а следовательно, все утверждения, которые содержатся в репликах, произносимых В., лживы. Отслеживая такие связи между высказываниями, можно восстановить исходное состояние проблемы, если в результате рассуждений мы придем к противоречию.
Естественно, что эти объекты можно представлять в программе по-разному. Онтологический анализ практически никогда не приводит к единственному способу представления. Для первой версии CLIPS-программы я выбрал следующее представление описанных объектов:
;;Объект statement (высказывание) связан с определенным
;;персонажем (поле speaker).
;;Высказывание содержит утверждение (поле claim).
;;Высказывание имеет основание - причину (поле reason),
;;по которой ему можно доверять,
;;и тэг (tag) - это может быть произвольный
;;идентификатор, (deftemplate statement
(field speaker (type SYMBOL))
(multifield claim (type SYMBOL))
(multifield reason (type INTEGER) (default 0))
(field tag (type INTEGER) (default 1)) )
(statement (speaker A) (claim F A))
Обратите внимание, что поля claim и reason имеют квалификатор multifield, поскольку они могут содержать несколько элементов данных (более подробно об этом рассказано в Руководстве пользователя).
Однако недостаточно только представить в программе высказывания персонажей — нам понадобится также выявить суть содержащихся в них утверждений. Далее, приняв определенное предположение о правдивости или лживости персонажа, которому принадлежит высказывание, можно построить гипотезу об истинности или лживости этого утверждения. С каждым таким утверждением свяжем уникальный числовой идентификатор.
Утверждение, смысл которого, например, состоит в следующем, ;;Т А ... означает, что А правдолюбец; ;;F А ... означает, что А лжец. ;;Утверждение может иметь под собой ;;основание (reason) - обычно это тэг ;;высказывания (объекта statement) или тэг ;;другого утверждения (объекта claim). ;;Утверждение также характеризуется признаком scope, ;;который может принимать значение "истина" или "ложь", (deftemplate claim
(multifield content (type SYMBOL))
(multifield reason (type INTEGER) (default 0))
(field scope (type SYMBOL)) )
(claim (content F A) (reason 1) (scope truth)).
Еще нам потребуется представление в программе того мира (world), в котором мы в настоящее время находимся. Объекты world порождаются в момент, когда мы формируем определенные предположения. Нужно иметь возможность различать разные множества предположений- и идентифицировать их в программе в тот момент, когда процесс размышлений приводит нас к противоречию. Например, противоречие между высказываниями Т(А) и F(A) отсутствует, если они истинны в разных мирах, т.е. при разных предположениях. Если у вас есть на сей счет сомнения, вернитесь вновь к примерам в самом начале раздела А.4.
Миры будем представлять в программе следующим образом:
;;Объект world представляет контекст,
;;сформированный определенными предположениями
;;о правдивости или лживости персонажей.
;;Объект имеет уникальный идентификатор в поле tag,
;;а смысл допущения - истинность или лживость -
;;фиксируется в поле scope,
(deftemplate world
(field tag (type INTEGER) (default 1))
(field scope (type SYMBOL) (default truth)) )
;; Утверждение, что А лжец.
(deffacts the-facts
(world)
(statement (speaker A) (claim FA)) )
А.4.3. Разработка правил
А.4.3. Разработка правил
В этом разделе мы рассмотрим набор правил, который помогает справиться с вырожденной формулировкой Р0 задачи о лжецах и правдолюбцах. Первые два правила, unwrap-true и unwrap-false, извлекают содержимое высказывания в предположении, что персонаж, которому принадлежит высказывание, является соответственно правдолюбцем или лжецом, и на этом основании формируют объект claim.
;; Извлечение содержимого высказывания,
(defrule unwrap-true
(world (tag ?N) (scope truth))
(statement (speaker ?X) (claim $?Y) (tag ?N)) =>
(assert (claim (content Т ?Х) (reason ?N)
(scope truth)))
(assert (claim (content $?Y) (reason ?M)
(scope truth)))
)
(defrule unwrap-false
(world (tag ?N) (scope falsity))
(statement (speaker ?X) (claim $?Y) (tag ?N)) =>
(assert (claim (content F ?X) (reason ?N)
(scope falsity)))
(assert (claim (content NOT $?Y) (reason ?N)
(scope falsity)) )
Далее нам понадобятся правила, которые введут отрицания в выражения. Поскольку —<Т(А) эквивалентно F(A), a —F(A) эквивалентно Т(А), то правила, выполняющие соответствующие преобразования, написать довольно просто. Анализ результатов применения этих правил значительно упростит выявление противоречий, следующих из определенного предположения.
;; Правила отрицания (defrule notl
?F <- (claim (content NOT Т ?Р)) =>
(modify ?F (content F ?P))
)
(defrule not2
?F <- (claim (content NOT F ?P)) =>
(modify ?F (content Т ?Р))
)
;; Выявление противоречия между предположением о
;; правдивости и следующими из него фактами,
(defrule contra-truth
(declare (salience 10))
?W <- (world (tag ?N) (scope truth))
?S <- (statement (speaker ?Y) (tag ?N))
?P <- (claim (content Т ?Х) (reason ?N) (scope truth))
?Q <- (claim (content F ?X) (reason ?N) (scope truth)) =>
(printout t crlf
"Statement is inconsistent if " ?Y " is a knight."
;; "Высказывание противоречиво, если " ?Y " правдолюбец."
t crlf)
(retract ?Q)
(retract ?P)
(modify ?W (scope falsity)) )
;; Выявление противоречия между предположением о
;; лживости и следующими из него фактами, (defrule contra-falsity
(declare (salience 10))
?W <- (world (tag ?N) (scope falsity))
?S <- (statement (speaker ?Y) (tag ?N))
?P <- (claim (content F ?X) (reason ?N) (scope falsity))
?Q <- (claim (content T ?X) (reason ?N)
(scope falsity)) => (printout t crlf
"Statement is inconsistent if " ?Y " is a knave. "
;; "Высказывание противоречиво, если " ?Y " лжец." t crlf)
(modify ?W (scope contra))
;; Удалить из базы фактов все утверждения,
;; которые следуют из предположения о правдивости.
(defrule sweep
(declare (salience 20))
(world (tag ?N) (scope falsity))
?F <- (claim (reason ?N) (scope truth)) =>
(retract ?F)
Если теперь запустить на выполнение программу, представив ей исходный набор фактов, соответствующих условию задачи РО, то программа обнаружит, что оба контекста противоречивы. Другими словами, независимо от того, предполагаем ли мы, что А говорит правду или лжет, программа обнаружит противоречие в контексте world. Трассировка программы в этом случае представлена в листинге А. 1. Строки, выведенные курсивом, — сообщения основной программы, а прочие — сообщения программы трассировки. Для удобства строки, указывающие на активизацию правил, представлены полужирным шрифтом.
А.4.4. Расширение набора правил — работа с составными высказываниями
А.4.4. Расширение набора правил — работа с составными высказываниями
Расширим теперь возможности программы таким образом, чтобы она могла работать с составными высказываниями. Это даст возможность охватить в ней не только вырожденный случай, рассмотренный в предыдущем разделе, но и более сложные. За основу возьмем следующую головоломку.
Р4. Встречаются два персонажа, А и В, каждый из которых либо лжец, либо правдолюбец. Персонаж А говорит: "Мы оба лжецы". К какой категории следует отнести каждого из них?
В этой задаче нам придется иметь дело с конъюнкцией, поскольку утверждение, высказанное персонажем А, моделируется выражением
Эту конъюнкцию нужно разделить на выражения-компоненты и проанализировать их непротиворечивость. Очевидно, что А не может быть правдолюбцем, поскольку это противоречит утверждению, которое содержится в его реплике. Но программа должна самостоятельно "распаковать" эту конъюнкция для того, чтобы прийти к такому выводу.
Нам также понадобится снабдить программу и средствами обработки дизъюнкции, поскольку, если предположить, что А лжет, нужно будет оперировать с отрицанием этого утверждения, которое преобразует выражение
Т(А) v Т(B).
Таким образом, в программу нужно включить правило выполнения отрицания составных высказываний и правило, которое "понимало" бы, что дизъюнкты вроде Т(А) в действительности являются предположениями. Составное выражение Т(А) v T(B) будем обрабатывать, предположив Т(А), и проанализируем, нет ли в нем противоречия. Если таковое не обнаружится, то можно предположить, что Т(А) v Т(B) совместимо с утверждением о том, что А лгун, т.е. F(A). Но если предположение Т(А) приведет к несовместимости, то нужно отказаться от него и предположить Т(Е). Если и это предположение приведет к несовместимости, то это означает, что утверждение Т(А) v T(B) несовместимо с предположением F(A). В противном случае Т(В) образует часть совместимой интерпретации исходного высказывания.
В CLIPS составные высказывания проще всего представлять с помощью так называемой "польской" (или префиксной) нотации операций. Суть этого способа представления операций состоит в том, что символ операции предшествует символам операндов. Каждый оператор имеет фиксированное количество операндов, а потому всегда существует возможность однозначно установить область действия операции даже в случае, если операнды представляют собой вложенные выражения. Таким образом, выражение, представленное скобочной формой —(F(/4) ^ Т(В)), в польской записи будет иметь вид
NOT AND F А Т В.
Задавшись таким способом представления составных высказываний, сформируем правило выполнения отрицания дизъюнктивной и конъюнктивной форм, в котором будет использоваться функция flip, заменяющая "Т" на "F" и наоборот.
(defrule not-or
?F <- (claim (content NOT OR ?P ?X ?Q ?Y)) =>
(modify ?F (content AND (flip ?P) ?X (flip ?Q) ?Y))
(defrule not-and
?F <- (claim (content NOT AND ?P ?X ?Q ?Y)) =>
(modify ?F (content OR (flip ?P) ?X (flip ?Q) ?Y)) )
NOT AND F А Т В
прямо к
OR Т A F В,
минуя
OR NOT F A NOT Т В.
(def function flip (?P)
(if (eq ?P Т) then F else T) )
Т(А) v Т(В)
или
но не будем использовать более сложные утверждения в форме
или
Наибольшие сложности при модификации нашей программы связаны с обработкой дизъюнктивных выражений, поскольку вывод о наличии противоречия может быть сделан только после завершения анализа всех членов операндов дизъюнкции. Например, нет противоречия между F(A) и Т(А) v F(B). Противоречие, которое обнаружится при обработке первого операнда дизъюнкции ДЛ) в предположении F(A), будет локальным в контексте Т(А). Но если мы вернемся к исходной дизъюнкции и попробуем проанализировать контекст F(B), то никакого противоречия обнаружено не будет, и, следовательно, интерпретация найдена.
Реализовать такой анализ локальных и глобальных противоречий можно, добавив в шаблон объекта claim атрибут context:
(def template claim
(multifield content (type SYMBOL))
(multifield reason (type INTEGER)
(default 0)) (field scope (type SYMBOL))
(field context (type INTEGER) (default 0)) )
T(A) v F(B)
причем Т(А) будет истинным в контексте 1, a F(B)— истинным в контексте 2. В этом случае все выражение будет истинным глобально, т.е. в контексте 0.
Структуру объекта world также нужно модифицировать — внести в нее поле context. Это позволит отслеживать ход вычислений. Пусть, например, объект world имеет вид
(world (tag 1) (scope truth) (context 2)).
истинно высказывание, имеющее идентификатор (tag), равный 1, и
правый операнд утверждения, которое содержится в этом высказывании, имеет значение "истина".
Новый вариант шаблона объекта world приведен ниже.
Объект world представляет контекст, сформированный определенными предположениями о правдивости или лживости персонажей.
Объект имеет уникальный идентификатор в поле tag, а смысл допущения - истинность или лживость - фиксируется в поле scope.
В поле context сохраняется текущий контекст анализируемого операнда дизъюнкции.
0 означает глобальный контекст дизъюнкции,
1 означает левый операнд,
2 означает правый операнд, (deftemplate world
(field tag (type INTEGER) (default 1))
(field scope (type SYMBOL) (default truth))
(field context (type INTEGER) (default 0)) )
(defrule left-or
?W <- (world (tag ?N) (context 0))
(claim (content OR ?P ?X ?Q ?Y)(reason ?N)
(scope ?V)) =>
(modify ?W (context 1)) (assert (claim
(content ?P ?X) (reason ?N) (scope ?V)
(context 1))) )
По этому же принципу разработаем правило для формирования контекста правого операнда дизъюнкции.
(defrule right-or
?W <- (world (tag ?N) (context 1))
(claim (content OR ?P ?X ?Q ?Y) (reason ?N)
(scope ?V)) =>
(modify ?W (context 2)) (assert (claim
(content ?Q ?Y) (reason ?N) (scope ?V) (context 2))
)
Упражнение 2
(claim (content AND Т A F В) (reason 1) (scope truth))
Далее разработаем правила, чувствительные к контексту, которые будут выявлять наличие противоречий в анализируемых утверждениях.
;; Выявление противоречия между предположением о
;; правдивости и следующими из него фактами
;; в разных контекстах одного и того же объекта world,
(defrule contra-truth-scope
(declare (salience 10)) (world (tag ?N)
(scope truth) (context ?T)) (claim
(content Т ?Х) (reason ?N) (scope truth)
(context ?S&:(< ?S ?T))) ?Q <- (claim
(content P ?x) (reason ?N)
(scope truth) (context ?T)) =>
(printout t "Disjunct " ?T
" is inconsistent with earlier truth context. "
;; "Дизъюнкт " ?T
;; " противоречит ранее установленному контексту правдивости.
" crlf)
(retract ?Q)
)
;; Выявление противоречия между предположением о
;; лживости и следующими из него фактами
;; в разных контекстах одного и того же объекта world.
(defrule contra-falsity-scope (declare (salience 10))
?W <- (world (tag ?N) (scope falsity)
(context ?T» (claim
(content F ?X) (reason ?N) (scope falsity)
(context ?S&:(< ?S ?T))) ?Q <- (claim (content Т ?Х)
(reason ?N)
(scope falsity) (context ?T)) =>
(printout t "Disjunct " ?T
" is inconsistent with earlier falsity context."
;; "Дизъюнкт " ?T
;; " противоречит ранее установленному контексту лживости . "
crlf)
(retract ?Q) )
Нам потребуется модифицировать и прежний вариант правила centra-truth.
;; Выявление противоречия между предположением о
;; правдивости и следующими из него фактами
;; в одном и том же контексте одного и того же объекта world .
(defrule contra-truth
(declare (salience 10))
?W <- (world (tag ?N) (scope truth))
?P <- (claim (content Т ?Х) (reason ?N)
(context ?S)
(scope truth) ) ?Q <- (claim (content F ?X)
(reason ?N) (context ?S)
(scope truth) ) =>
(printout t
"Statement is inconsistent if "?X " is a knight"
;; "Высказывание противоречиво, если " ? X
;; " правдолюбец . "
crlf)
(retract ?Q) (retract ?P) (modify ?W (scope falsity) (context 0)
;; Выявление противоречия между предположением о
;; лживости и следующими из него фактами
;; в одном и том же контексте одного и того же объекта world.
(defrule contra-falsity (declare (salience 10))
?W <- (world (tag ?N) (scope falsity))
?P <- (claim (content F ?X) (reason ?N)
(context ?S) (scope falsity))
?Q <- (claim (content Т ?Х) (reason ?N)
(context ?S)(scope falsity)) =>
(printout t
"Statement is inconsistent whether " ?X
" is knight or knave."
;; "Высказывание противоречиво, независимо от того,"
;; "является ли " ?Х " правдолюбцем или лжецом."
crlf)
(modify ?W (scope contra) )
(defrule consist-truth
(declare (salience -10))
?W <- (world (tag ?N) (scope truth))
(statement (speaker ?Y) (tag ?N)) =>
(printout t
"Statement is consistent:"
;; "Высказывание непротиворечиво:"
crlf)
(modify ?W (scope consist)
)
(defrule consist-falsity
(declare (salience -10)) ?W <- (world (tag ?N)
(scope falsity)) (statement (speaker ?Y) (tag ?N)) =>
(printout t
"Statement is consistent:"
;; "Высказывание непротиворечиво:"
crlf) (modify ?W (scope consist)
)
(defrule true-knight
(world (tag ?N) (scope consist))
?C <- (claim (content T ?X) (reason ?N) =>
(printout t
?X "is a knight" ;; ?X "- правдолюбец"
crlf) (retract ?C)
)
(defrule false-knave
(world (tag ?M) (scope consist))
?C <- (claim (content F ?X) (reason ?N))
(printout t
?X " is a knave" ;; ?X "- лжец"
crlf)
(retract ?C) )
(defrule conj
(world (tag ?N (context ?S))
(claim (content AND ?P ?X ?Q ?Y) (reason ?N)
(scope ?V)) =>
(assert (claim
(content ?P ?X) (reason ?N) (scope ?V)
(context ?S))) (assert (claim
(content ?Q ?Y) (reason ?N) (scope ?V)
(context ?S))) )
(deffacts the-facts
(world)
(statement (speaker A) (claim AND F A F B) (tag 1)) )
A.4.5. Обратное прослеживание и множество контекстов
A.4.5. Обратное прослеживание и множество контекстов
Модифицируем программу таким образом, чтобы она могла справиться и с задачами этого класса в более сложной постановке. Речь идет о задачах, в которых несколько персонажей произносят реплики. Пример такого рода головоломки приведен ниже.
Упражнение 3
К какой категории следует отнести каждый из персонажей? (Решите эту задачу самостоятельно вручную, используя ту же систему обозначений, которая применялась ранее в этом Приложении.)
От программы потребуется выполнить обратное прослеживание (откат) в следующих ситуациях:
когда обнаружится конфликт между текущим "миром" и ранее существовавшим, причем в ранее существовавшем "мире" предполагается истинность высказывания, но не была проанализирована его лживость;
когда обнаружится конфликт между текущим "миром" и ранее существовавшим, причем в ранее существовавшем "мире" был проанализирован только один операнд в составном дизъюнктивном утверждении.
Чтобы смысл этих формулировок стал более понятным, рассмотрим следующий пример. Р6. Встречаются два человека, А и В, которые заявляют следующее.
А: "Хотя бы один из нас говорит правду". В: "Хотя бы один из нас лжец". К какой категории следует отнести каждый из персонажей?
Высказывания персонажей представим в следующем виде:
А: Т(А) v Т(В) В: F(A) v F(B)
и проанализируем левый операнд дизъюнкции. В результате будет сформирована корректная непротиворечивая интерпретация: В — правдолюбец, А — лжец.
Получив непротиворечивую интерпретацию высказывания персонажа В, перейдем к анализу высказывания персонажа А:
Т(А) => FALSE,
поскольку правдивость А противоречит сформированной ранее интерпретации высказывания персонажа В. Предположим, что А — лжец. Тогда:
F(A) => -(T(А) v T(B)) => F(A) ^ F(B) => FALSE.
Таким образом, оказывается, что это предположение также не работает, поскольку противоречит выбранной ранее интерпретации высказывания персонажа В , из которой следует, что В говорит правду,
Но анализ высказывания персонажа В нельзя считать законченным, поскольку не был выполнен анализ правого операнда дизъюнкции
N(B) => F(A) v F(B)
Поэтому придется вернуться назад в ту точку процесса логического анализа, где было сделано предположение об истинности левого операнда в дизъюнкции, и проанализировать вместо него правый операнд F(B). При этом сразу же будет обнаружено противоречие между истинностью F(B) и ранее высказанным предположением о правдивости персонажа В, но, не вернувшись назад и не выполнив этот анализ, мы не смогли бы обнаружить это противоречие. Теперь остается проанализировать следствие из предположения, что В — лжец.
F(B) => -(F(A) v F(B)) => Т(А) ^T(В) => FALSE.
Только теперь можно с чистой совестью утверждать, что не существует непротиворечивой интерпретации высказываний, приведенных в условии задачи. Предположение о правдивости персонажа В приводит к конфликту с высказыванием персонажа А, а предположение о лживости В противоречит его же словам.
Чтобы в системе, использующей правила в качестве основного программного компонента, реализовать откат (обратное прослеживание), нужно в первую очередь иметь возможность восстановить тот контекст, который существовал в момент, когда было сформулировано предположение, приведшее к не удовлетворяющему нас результату. Как было показано в главе 5, одно из достоинств продукционных систем, подобных CLIPS, состоит в том, что они способны выполнить такой откат, не сохраняя прежнего состояния процесса вычислений, что коренным образом отличает их от фундаментально рекурсивных языков программирования, таких как LISP и PROLOG. При возникновении необходимости выполнить откат продукционные системы последовательно отменяют в обратном порядке все операции, связанные с добавлением данных в рабочую память, которые были выполнены, начиная с точки возврата, в которую нужно вернуться, вплоть до текущего этапа вычислений. Но таким способом можно реализовать возврат, только предполагая, что в ходе выполнения операций, следующих за точкой возврата, из рабочей памяти не было удалено ничего существенного, а все действия, модифицирующие состояние рабочей памяти, носили исключительно аддитивный характер.
Примеры, подобные задаче Р6, существенно усложняют жизнь, поскольку для их решения программа должна выполнять некоторые дополнительные операции, в которых не было необходимости при решении задач с единственным высказыванием.
(1) Сохранять информацию о возможных точках возврата.
(2) При обнаружении противоречия принимать решение, выполнять или не выполнять откат, а если выполнять, то в какую именно точку.
(3) Отменить все изменения, внесенные в состояние рабочей памяти после "прохождения" выбранной точки возврата.
(4) Возобновить вычисления начиная с точки возврата. Рассмотрим подробнее каждую из этих операций.
Каждый объект world имеет уникальный числовой идентификатор, который хранится в поле tag. Эта информация практически не используется при решении задач с единственным высказыванием, поскольку мы всегда имеем дело с одним и тем же объектом world, связанным с этим высказыванием. Но при решении задач, оперирующих с несколькими высказываниями, нам придется различать утверждения, которые порождены разными высказываниями в разных "мирах". По мере того, как мы будем переходить от анализа одних высказываний к другим, будут формироваться и новые объекты world. Прежние объекты world нужно оставлять в таком состоянии, чтобы при необходимости к ним можно было еще раз вернуться. Это означает, что вектор world, с которым прекращены операции (возможно, временно), содержал всю информацию, которая потребуется программе для возобновления работы с ним. При этом именно та точка, в которой процесс вычислений "переключился" на новый объект world, и будет потенциальной точкой возврата. Информация, сохраняемая в объекте, включает знание о том, какое предположение о правдивости или лживости персонажа было сделано в этом "мире" и какие дизъюнкты (операнды составного дизъюнктивного выражения) в утверждении, содержащемся в высказывании персонажа, уже проанализированы.
Поскольку каждый объект world имеет свой уникальный идентификатор и каждое утверждение (объект claim) индексировано определенным объектом world, можно довольно просто выяснить, существует ли противоречие между разными "мирами" (т.е. между утверждениями, связанными с разными объектами world). Остается единственный вопрос — нужно ли возвращаться в ранее покинутый "мир", если в текущем "мире" обнаружено противоречие с ним. Мы будем применять стратегию поиска в глубину, которая состоит в том, что откат нужно выполнять только в том случае, если противоречие сохраняется после полного завершения анализа текущего "мира".
Если объекты world нумеруются последовательно, по мере их формирования, то потребуется разработать правило, которое при возвращении в покинутый ранее "мир" уничтожит как текущий объект world, так и все промежуточные объекты такого типа, которые при необходимости затем могут быть воссозданы.
Если прежний объект world содержит полную информацию о том, в каком состоянии был покинут "мир", и утверждения в этом "мире" не противоречат этому состоянию, то ничто не мешает нам продолжить вычисления из точки возврата.
Начнем модификацию нашей программы с того, что в шаблон объекта world включим слот, в котором будет храниться идентификатор ранее покинутого "мира" (объекта), с которым данный объект конфликтует. Это нужно сделать по двум пр'ичинам.
(1)Нам потребуется различать случаи, в которых противоречия возникают в пределах одного и того же "мира", от конфликтов между "мирами". Если текущее высказывание само по себе противоречиво (т.е. является парадоксом), нет смысла выполнять откат в прежний мир и искать в нем разрешения противоречия.
(2) Наличие такого слота позволит разработать правило, которое будет выполнять откат прямо в этот покинутый ранее "мир".
Ниже будет показано, что для решения проблемы можно обойтись без реализации правила, упомянутого в п.2, хотя это и не так легко сделать, но соображения, высказанные в п.1, в любом случае остаются в силе.
Объект world представляет контекст, сформированный определенными предположениями о правдивости или лживости высказывания, принадлежащего некоторому персонажу. Объект имеет уникальный идентификатор в поле tag, а смысл допущения - истинность или лживость -фиксируется в поле scope. Поле prior может содержать идентификатор объекта world, обработанного перед тем, как был создан данный объект, и с которым данный объект может потенциально конфликтовать. В поле context сохраняется текущий контекст анализируемого операнда дизъюнкции, (deftemplate world
(field tag (type INTEGER) (default 1))
(field scope (type SYMBOL) (default truth))
(field prior (type INTEGER) (default 0))
(field context (type INTEGER) (default 0)
CHECK. Эта операция реализует нормальный режим выполнения вычислений при анализе предположений о правдивости или лживости.
CONTRA. Анализ обнаруженного противоречия. Возникло ли оно между двумя высказываниями в одном и том же "мире"? Возникло ли противоречие между двумя высказываниями в одном и том же "мире", но в разных контекстах, например в разных операндах дизъюнкции? Возникло ли оно между двумя разными "мирами", производными от высказываний разных персонажей?
CLEAN. После того, как выявлен характер возникшего противоречия, и перед тем, как выполнить откат в точку возврата, эта операция удаляет все утверждения, созданные в текущем "мире".
BACK. Если мы имеем дело с противоречием между текущим "миром" и ранее покинутым, эта операция выполняет возврат в ранее покинутый "мир", в котором не был полностью завершен анализ всех дизъюнктов или не было проанализировано предположение о лживости.
QUIT. Нам потребуется обнаружить ситуацию, которая наступает в случае, когда проанализированы все возможные интерпретации множества высказываний, т.е. все дизъюнктивные ветви и все возможные комбинации предположений о правдивости или лживости высказываний. Если при обнаружении такой ситуации не удалось найти непротиворечивую интерпретацию, можно со всей ответственностью утверждать, что условия задачи сами по себе несовместны, т.е. не существует ее решения в терминах отнесения каждого из персонажей к определенной категории — к лжецам или к правдолюбцам.
Еще раз модифицируем определение шаблона объекта world — внесем в него поле TASK, в котором будут представлены перечисленные задачи. Это поле будет использовано правилами, которые нам еще предстоит разработать. Механизм работы с задачами подобен тому, который использовался для манипулирования лексемами управления (control tokens), описанными в главах 5 и 14. Этот механизм активизирует определенные правила. Однако при этом мы не будем использовать стратегию МЕА или специальные векторы. Лексемы управления будут просто сохраняться в определенном поле объекта world. Но результат будет тот же — эта лексема будет использована для активизации определенного правила.
;;Объект world представляет контекст,
;;сформированный определенными предположениями
;;о правдивости или лживости высказывания,
;;принадлежащего некоторому персонажу.
;;Объект имеет уникальный идентификатор
;;в поле tag, который соответствует
;;тэгу высказывания.
;;Смысл допущения - истинность или лживость -
;;фиксируется в поле scope.
;;Поле TASK содержит одно из перечисленных
;;ниже значений:
;; BACK - откат в точку возврата
;; QUIT - прекращение процесса.
;;Поле prior может содержать идентификатор
;;объекта world, обработанного перед тем,
;;как был создан данный объект, и с которым данный
;;объект может потенциально конфликтовать.
;;В поле context сохраняется текущий контекст
;;анализируемого операнда дизъюнкции,
(deftemplate world
(field tag (type INTEGER) (default 1))
(field scope (type SYMBOL) (default truth))
(field task (type SYMBOL) (default check))
(field prior (type INTEGER) (default 0))
(field context (type INTEGER) (default 0)) )
Выявление противоречий
между высказываниями в одном и том же "мире", но, возможно, в разных контекстах дизъюнктивного утверждения;
между высказываниями в разных "мирах".
Для анализа вариантов возникновения противоречий целесообразно разработать четыре правила. Разобьем первую из указанных ситуаций на два случая:
обнаруживается противоречие между предположением и высказыванием, которые существуют в одном и том же контексте (например, если из предположения Т(А) непосредственно следует заключение F(A), как в заявлении персонажа A: F(A));
обнаруживается противоречие между предположением и одним из дизъюнктов составного высказывания, как в заявлении персонажа А: 1\В) v F(A).
Ситуацию, когда противоречие существует между высказываниями в разных "мирах", тоже можно разделить на два случая:
текущий "мир" рассматривается в предположении, что персонаж говорит правду (в поле scope объекта world содержится значение truth);
текущий "мир" рассматривается в предположении, что персонаж лжет (в поле scope объекта world содержится значение falsity).
Если, предположив правдивость персонажа, программа обнаружит противоречие, она должна проанализировать и следствие из противоположного предположения — что персонаж лжец. И только при условии, что оба варианта предположения приводят к противоречию, нужно выполнить откат.
Анализ каждого из четырех вариантов ситуации выполняется отдельным правилом, программы которых представлены ниже. Обратите внимание, что все правила имеют довольно высокий приоритет (значение параметра salience). Это обеспечивает их первоочередную активизацию механизмом разрешения конфликтов между правилами. Кроме того, правила, анализирующие противоречие в пределах одного и того же "мира", имеют более высокий приоритет, чем правила, анализирующие противоречие между разными "мирами". Тем самым обеспечивается реализация стратегии по возможности избегать откатов в процессе решения проблемы.
ЕСЛИ обнаруживается противоречие между предположением и производными от него фактами в пределах одного и того же "мира" и в одном и том же контексте, ТО зафиксировать противоречие и удалить противоречивые утверждения (объекты claim) из базы фактов. (defrule contradiction
(declare (salience 100))
?W <- (world (tag ?N) (task check) (context ?S)
(prior 0)) ?P <- (claim
(content ?F ?X) (reason ?N)
(context ?S)) ?Q <- (claim
(content ?GS:(not (eq ?G ?F)) ?X)
(reason ?N) (context ?S)) =>
(printout t crlf
"CONTRADICTION: " ?F ?X " versus " ?G ?X "in world " ?K
;; "ПРОТИВОРЕЧИЕ между: " ?F ?X " и "?G ?X "в мире " ?N
t crlf) (retract ?P) (retract ?Q) (modify ?W (task contra))
)
;; ЕСЛИ обнаруживается противоречие между предположением
;; и производными от него фактами в пределах одного и
;; того же "мира", но в разных контекстах,
;; ТО зафиксировать противоречие.
(defrule transcontext
(declare (salience 90))
?W <- (world (tag ?N) (task check) (context ?T)
(prior 0)) (claim (content ?F ?X) (reason ?N)
(context ?S:(< ?S ?T))) (claim
(content ?GS:(not (eq ?G ?F)) ?x)
(reason ?N) (context ?T)) =>
(printout t crlf
"TRANSCONTEXT CONTRADICTION: " ?F ?X " versus
?G ?X " in world " ?N
"ТРАНСКОНТЕКСТНОЕ ПРОТИВОРЕЧИЕ между: " ?F ?X
;;" и "?G ?X "в мире " ?N
t crlf) (modify ?W (task contra))
)
; ; ЕСЛИ обнаруживается противоречие между
;; текущим "миром" в предположении о правдивости
;; и ранее покинутым "миром" ,
;; ТО зафиксировать противоречие.
(defrule transworld-truth (declare (salience 80))
?W <- (world (tag ?N) (scope truth) (task check)
(prior 0))
(claim (content ?F ?X) (reason ?N))
(claim (content ?G&:(not (eq ?G ?F)) ?X) (reason ?M&:(< ?M ?N))) =>
(printout t crlf
"TRANSWORLD CONTRADICTION: " ?F ?X "
versus ?G ?X " in worlds " ?N "|" ?M
;; "МЕЖМИРОВОЕ ПРОТИВОРЕЧИЕ: " ?F ?X "
противоречит ; ; ?G ?X " в мирах " ?N " | " ?M
t crlf) (modify ?w (task contra))
)
;; ЕСЛИ обнаруживается противоречие между
;; текущим "миром" в предположении о лживости
;; и ранее покинутым "миром",
;; ТО подготовиться к выполнению отката в ранее
;; покинутый "мир" .
(defrule transworld-falsity
(declare (salience 80))
?W <- (world (tag ?N) (scope falsity)
(task check)) (claim (content ?F ?X)
(reason ?N)) (claim (content ?G&:(not (eq ?G ?F)) ?X)
(reason ?MS:(< ?M ?N))) =>
(printout t crlf
"TRANSWORLD CONTRADICTION: " ?F ?X " versus
" ?G ?X " in worlds " ?N " | " ?M
;; "МЕЖМИРОВОЕ ПРОТИВОРЕЧИЕ: " ?F ?X "
противоречит " ;; ?G ?X " в мирах " ?N "|" ?M
t crlf) (modify ?W (task contra) (prior ?M) )
Подготовка рабочей памяти к выполнению отката
если к противоречию привел выбор определенного дизъюнкта, нужно удалить контекст, созданный в результате этого выбора;
если к противоречию привело предположение о правдивости персонажа, нужно удалить соответствующий контекст и проанализировать, к чему приведет противное предположение.
В любом случае из рабочей памяти нужно удалить объекты claim. Поскольку откат никогда не выполняется в противоречивом контексте, такое удаление не повлияет на полноту представления информации о задаче. Если потребуется вновь сформировать удаленный контекст при других предположениях, это можно будет выполнить, повторив вычисления с "чистого листа".
;; ЕСЛИ обнаружено противоречие с одним из
;; дизъюнктивных контекстов "мира",
;; ТО удалить все утверждения (объекты claim)
;; этого контекста.
;; ПРИМЕЧАНИЕ: правило будет активизироваться повторно,
;; пока не будут удалены все ненужные объекты,
(defrule clean-context
(declare (salience SO))
(world (tag ?N) (task contra) (prior 0)
(context ?S&~0))
?F <- (claim (reason ?N) (context ?S)) =>
(retract ?F) )
;; ЕСЛИ противоречие обнаружено в текущем "мире" в
;; предположении о правдивости,
;; ТО повторить анализ, предположив лживость персонажа.
(defrule switch-context
(declare (salience 40))
;; Если больше нет правых дизъюнктов,
?W <- (world (tag ?N) (scope truth) (task contra)
(prior 0) (context ?S&"1)) =>
;; изменить предположение и сформировать новый контекст.
(modify ?W (scope falsity) (task check) (context 0))
)
;; Удалить все утверждения (объекты claim),
;; сформированные на основании предположения о
;; правдивости.
;; ПРИМЕЧАНИЕ: правило будет активизироваться повторно,
;; пока не будут удалены все ненужные объекты,
(defrule sweep-truth
(declare (salience 100))
(world (tag ?N) (scope falsity))
?F <- (claim (reason ?N) (scope truth))-=>
(retract ?F) )
Теперь можно приступить к разработке правил, выполняющих откат.
Выполнение отката
Если текущий "мир" был проанализирован только в предположении о правдивости соответствующего персонажа, то, очевидно, нужно повторить его анализ, но уже в предположении, что персонаж — лжец. Если и в этом случае конфликтная ситуация сохраняется, следовательно, мы исчерпали все возможности ее локального разрешения, поэтому нужно подумать о возврате к анализу ранее покинутого "мира" и повторить его анализ, но уже на основе других предположений.
Но возврат имеет смысл выполнять только в том случае, если в ранее покинутом "мире" не были проанализированы все возможные сочетания предположений и контекстов. Таким образом, одно из условий, при которых целесообразно выполнять откат, состоит в том, что покинутый "мир" был проанализирован только в предположении правдивости соответствующего персонажа и что есть еще возможность проанализировать следствия из противоположного предположения.
Правило undirected-falsity выполняет необходимые для этого подготовительные операции. Смысл слова undirected (ненаправленный) состоит в том, что это правило реализует откат в "хронологическом" порядке создания "миров". В механизме разрешения конфликтов в CLIPS реализовано "хронологическое предпочтение", которое обеспечивает откат к последнему из ранее сформированных "миров", удовлетворяющих заданным условиям. Но при этом не предпринимается никакой попытки локализовать в процессе выбор точки, которая стала причиной конфликта, например выбрать именно тот "мир", с которым конфликтует текущий.
;; Хронологический откат к тому "миру", который был
;; покинут без выполнения анализа в предположении
;; о лживости (поле scope содержит значение truth,
;; а поле task - значение check),
(defrule undirected-falsity
(world (tag ?N) (scope falsity) (task contra))
?W <- (world (tag, ?M&:(< ?M ?N)) (scope truth) (task check))
=>
(modify ?W (task back)) )
Приведенное ниже правило undirected-disjunct выполняет подготовку к такому откату в хронологическом порядке.
;; Хронологический откат к тому "миру", который был
;; покинут без завершения анализа дизъюнктов,
(defrule undirected-disjunct
world (tag ?N) (scope falsity) (task contra))
V <- (world (tag ?M&:(< ?M ?N))
(task check) (context 1))
claim (content OR ?P ?X ?Q ?Y) (reason ?M)
(scope ?S)) =>
;; Дизъюнкт в ранее покинутом "мире", анализ которого
;; не был выполнен.
assert (claim (content ?Q ?Y) (reason ?M) (scope ?S)
(context 2)))
;; Зафиксировать необходимость отката в этот "мир".
modify ?V (task back)) )
Если обнаружено противоречие между объектами world M и N и объект М создан ранее объекта N, причем анализ М в предположении о лживости соответствующего высказывания не был выполнен, ТО вернуться к анализу объекта М.
(defrule directed-falsity
(world (tag ?N) (scope falsity)
(task contra) (prior ?M&"0))
?W <- (world (tag ?M) (scope truth) (task check))
=>
(modify ?W (task back)) )
;; Если обнаружено противоречие между
;; объектами world М и N
;; и объект М создан ранее объекта N, причем
;; не был выполнен анализ всех дизъюнктов в М,
;; ТО вернуться к анализу объекта М.
(defrule directed-disjunct
(world (tag ?N) (scope falsity)
(task contra) (prior ?MS~0))
?V <- (world (tag ?M) (task check) (context 1))
(claim (content OR ?P ?X ?Q ?Y) (reason ?M)
(scope ?S)) =>
;; Дизъюнкт в ранее покинутом "мире", анализ которого
;; не был выполнен.
(assert (claim (content ?Q ?Y) (reason ?M)
(scope ?S) (context 2)))
;; Зафиксировать необходимость отката в этот "мир".
(modify ?V (task back)) )
Упражнение 4
А: "В лжец".
В: "С лжец".
С: "В говорит правду".
Как выполнить откат в процессе решения этой задачи. Отыщите "мир", в который нужно выполнить откат, и объясните, почему этот "мир" не удастся определить с помощью приведенных выше правил.
Фактически второе и третье высказывания в этой задаче несовместимы, а потому в ней не существует варианта непротиворечивого распределения ролей между персонажами.
Правила directed- и undirected- можно использовать в комбинации, нр с первого взгляда трудно решить, как именно это сделать. Условные части правил undirected-falsity и directed-falsity одинаковы, а потому нам остается только манипулировать значением параметра salience. Обычно "направленные" варианты правил более эффективны, но в результате их применения может пострадать полнота исследования "миров" в особо хитроумных задачах. "Ненаправленные" варианты работают медленнее, но зато обеспечивают исчерпывающий просмотр всех имеющихся в задаче объектов world. Я предлагаю читателям самостоятельно поэкспериментировать с обоими вариантами при решении разных задач рассматриваемого класса. Мы же в дальнейшем будем использовать только "ненаправленные" варианты этих правил.
Восстановление контекста
Удаление объектов world. ЕСЛИ выполняется откат к объекту М, ТО удалить все объекты world, имеющие идентификатор, больший М. ПРИМЕЧАНИЕ: правило может активизироваться несколько раз. (defrule undo-world
(declare (salience 40))
(world (tag ?M) (task back))
?W <- (world (tag ?N&:(> ?N ?M)))
?S <- (statement (tag ?N) (done ?X&"0)) =>
(retract ?W)
(modify ?S (done 0))
)
;; Удаление объектов claim.
; ; ЕСЛИ выполняется откат к объекту world M,
; ; ТО удалить все объекты claim,
; ; связанные с удаленными объектами world .
(defrule unclaim
(declare (salience 30))
(world (tag ?M) (task back))
?F <- (claim (reason ?N&:(> ?N ?M))) =>
(retract ?F)
)
(declare (salience 20) )
?W <- (world (tag ?M) (scope truth)
(task back) (context ?C&~1)) =>
(retract ?W)
(modify ?W (scope falsity)
(task check) (context 0))
Организация процесса вычислений на основе этих правил позволяет программе продолжать вычисления до тех пор, пока имеются не до конца проанализированные "миры". При использовании "ненаправленных" правил отката программа всегда сможет вернуться к последнему объекту world, предлагающему альтернативный вариант предположения, или к такому объекту, в котором имеются еще не проанализированные дизъюнкты составного утверждения.
Упражнение 5
Р7. Встретились два человека, А и В , которые заявляют следующее. А: "В утверждает, что он правдолюбец". В: "А утверждает, что он лжец".
К какой категории следует отнести каждый персонаж? Как следует модифицировать нашу программу, чтобы она могла решать задачи такого вида? Ниже будут высказаны некоторые соображения, которые подскажут вам, как модифицировать модель для таких задач.
В существующей программе мы прежде всего анализировали непротиворечивость каждого из "миров", т.е. внутреннюю непротиворечивость каждого отдельного высказывания. После этого мы анализировали непротиворечивость одного "мира" другому (или другим).
Включение в постановку задачи высказывания о высказывании (назовем его ме-тавысказыванием) несколько усложняет положение дел. В такой постановке высказывание образует "мир", в котором другое выказывание, а не утверждение, может быть истинным или ложным. Это второе, внутреннее, высказывание должно также формировать собственный "мир".
Рассмотрим высказывание
А: "В утверждает, что он правдолюбец".
Предположив, что А говорит правду, мы сформируем "мир", в котором В действительно утверждает, что он правдолюбец, но внутри этого "мира" существует и другой, в котором В действительно является правдолюбцем. Отслеживать подобного рода связи между внешним высказыванием и "внедренным" в него внутренним высказыванием — одна из новых задач механизма обработки правдоподобия.
А что можно сказать о "мире", в котором А лжет? Мы должны показать, что заявление В о том, что он лжец, приведет к противоречию.
А.4.6. Обработка метавысказываний
А.4.6. Обработка метавысказываний
Имея в своем распоряжении программу, снабженную механизмом реализации откатов, попробуем адаптировать ее к работе с метавысказываниями, т.е. высказываниями о высказываниях. Это позволит нам решать задачи, подобные Р7, которая приведена в упр. 5.
Рассмотрим высказывание:
А: "В утверждает, что он правдолюбец".
Мы должны сформировать "мир", в котором В утверждает, что он правдолюбец, а внутри этого "мира" другой, в котором В действительно является правдолюбцем. Такие внедренные "миры" образуют отдельное множество зависимостей, которое придется отслеживать с помощью механизма обработки правдоподобия. Начнем с того, что модифицируем шаблон объекта world и внесем в него информацию о том, является ли данный объект внешним или внутренним, а если внутренним, то какой объект world является для него внешним.
В более сложных сценариях работы с "мирами" нам потребуется также отслеживать, был ли данный объект world проанализирован полностью. Это упростит механизм выполнения отката.
;;Объект world представляет множество утверждений,
;;сформированных при определенном предположении
;;о правдивости или лживости высказывания,
;;принадлежащего некоторому персонажу.
;;Объект имеет уникальный идентификатор
;;в поле tag, который соответствует
;;тэгу высказывания.
;;Смысл допущения - истинность или лживость -
;;фиксируется в поле scope.
;;Поле TASK содержит одно из перечисленных
;;ниже значений:
;;CHECK - анализ предположений о
;;правдивости или лживости высказывания;
;;CONTRA - анализ обнаруженного противоречия;
;;CLEAN - удаляет все утверждения, созданные
;;в противоречивом "мире" ;
;;BАСК - откат в точку возврата;
;;QUIT - прекращение процесса.
;;Поле prior может содержать идентификатор
;;объекта world, обработанного перед тем,
;;как был создан данный объект, и с которым данный
;;объект может потенциально конфликтовать.
;;Поле upper содержит идентификатор другого объекта
;;world, в который внедрен данный объект, если
;;соответствующее высказывание содержит другое
;;высказывание.
;;Например, А говорит, что В сказал, что А - лжец.
;;В поле context сохраняется текущий контекст
;;анализируемого операнда дизъюнкции.
;;Поле done содержит информацию о том, обработано ли
;;уже высказывание, на основании которого создан этот
;;объект.
(deftemplate world
(field tag (type INTEGER) (default 1))
(field scope (type SYMBOL) (default truth))
(field task (type SYMBOL) (default check))
(field prior (type INTEGER) (default 0))
(field upper (type INTEGER) (default 0))
(field context (type INTEGER) (default 0))
(field done (type INTEGER) (default 0))
;;Объект statement (высказывание) связан с определенным
;;персонажем (поле speaker).
;;Высказывание содержит утверждение (поле claim).
;;Высказывание имеет основание - причину (поле reason).
;;Если данный объект не является производным от другого
;;объекта statement, в поле reason устанавливается
;;значение 0.
;;В поле tag устанавливается уникальный числовой
;;идентификатор объекта - число, большее 0.
;;В поле DONE устанавливается одно из
;;следующих значений:
0 означает, что объект еще не обрабатывался;
;;1 означает, что объект обрабатывался в предположении
;;о правдивости высказывания;
;;2 означает, что объект обрабатывался в предположении
;;о лживости высказывания. (deftemplate statement
(field speaker (type SYMBOL))
(multifield claim (type SYMBOL))
(field scope (type SYMBOL) (default truth))
(multifield reason (type INTEGER) (default 0))
(field tag (type INTEGER) (default 0))
(field done (type INTEGER) (default 0)) )
;;Теперь разработаем правило, которое будет
;;"распаковывать" высказывание о высказывании.
;; ЕСЛИ объект world базируется на предположении о
;; правдивости метавысказывания,
;; ТО предположить, что персонаж говорит правду и что
;; высказывание истинно.
(defrule unwrap-true-state
?W <- (world (tag ?N) (scope truth) (task check)
(done 0)) ?S <- (statement (speaker ?X) (claim SAY ?Z $?Y)
(done 0)) =>
(printout t crlf "Assuming " Т ?X " and " ?Z " says " $?Y
" in world " ?N
;; "Предполагается " Т ?X " и " ?Z " говорит " $?Y
;; "в мире " ?N
t crlf
)
;; Зафиксировать, что высказывание было распаковано
;; в предположении о его правдивости,
(modify ?S (tag ?N) (done 1))
;; Предположим, что персонаж в текущем "мире" является
;; правдолюбцем.
(assert (claim (content T ?X) (reason ?N)
(scope truth)))
;; Зафиксировать в объекте world, что высказывание
;; распаковано, (modify ?W (done 1))
;; Сформировать новый объект world для внедренного
;; высказывания и зафиксировать, что этот объект
;; является внутренним по отношению к объекту ?N.
(assert (world (tag ( + ?N 1)) (scope truth) (upper ?N)))
;; Зафиксировать внедренное высказывание в новом
;; объекте world.
(assert (statement (speaker ?Z) (claim $?Y) (reason ?N)))
)
;; ЕСЛИ объект world базируется на предположении о
;; лживости метавысказывания,
;; ТО предположить, что персонаж лжет.
;; Каких-либо предположений об истинности
;; утверждения не делается.
(defrule unwrap-false-state
?W <- (world (tag ?N) (scope falsity)
(task check)) ?S <- (statement (speaker ?X)
(claim SAY ?Z $?Y)
(tag ?N) (done 1)) =>
(printout t crlf "Assuming " F " "?X " and
NOT " ?Z " says " $?Y
" in. world " ?N
;; "Предполагается " F " "?X " и HE " ?Z "
говорит " $?Y ;; " в мире " ?N t crlf
)
;; Изменить значение в поле scope текущего объекта
;; world.
(modify ?W (scope falsity) (done 2))
;; Зафиксировать, что высказывание было распаковано
;; в предположении о лживости, (modify ?S (scope falsity) (done 2))
;; Предположить, что в текущем "мире" персонаж,
;; произнесший метавысказывание, лжец.
(assert (claim (content F ?X)
(reason ?N) (scope falsity))) )
А: "В не говорил, что он правдолюбец".
Более того, если А говорит, что В заявил нечто, то по условиям, принятым в головоломках этого класса, для того чтобы доказать, что А лжец, требуется только показать, что не существует непротиворечивого "мира", в котором В мог бы сделать правдивое утверждение. Таким образом, нам не придется обрабатывать отрицания в метавысказыва-ниях и анализировать их непротиворечивость. Указанные условия нашли свое отражение в правиле unwrap-false-state. В этом правиле которое активизируется, когда предположение о правдивости персонажа не срабатывает, просто предполагается, что этот персонаж лжет, а более глубокий анализ не проводится.
А.4.7. Полный листинг программы
А.4.7. Полный листинг программы
Ниже приведен полный листинг CLIPS-программы, которая обеспечивает обработку составных утверждений и метавысказывании.
Эта программа предназначена для разрешения головоломок о правдолюбцах и лжецах. Программа анализирует наличие логических противоречий и отыскивает первую непротиворечивую интерпретацию.
Программа может обрабатывать конъюнктивные и дизъюнктивные составные утверждения. Программа может решать задачи с множеством высказываний и метавысказываниями.
TEMPLATES
Объект CLAIM имеет следующие поля: CONTENT - содержимое утверждения, например, Т А ... означает, что А является правдолюбцем; F A ... означает, что А является лжецом; OR Т A F В ... означает, что А является правдолюбцем или В является лжецом, и т.д. REASON указывает, на основании какого высказывания сформировано данное утверждение. Значение этого поля равно идентификатору (полю tag) соответствующего объекта statement (высказывание). CONTEXT ::= 0 / 1 / 2; значение 0 означает глобальный контекст, значение 1 означает локальный контекст левого операнда, значение 2 означает локальный контекст правого операнда дизъюнкции,
(deftemplate claim
(multifield content (type SYMBOL))
(field reason (type INTEGER))
(field scope (type SYMBOL))
(field context (type INTEGER) (default 0))
)
;; Объект statement (высказывание) связан с определенным
персонажем (поле speaker).
;;Высказывание содержит утверждение (поле claim).
;;Высказывание имеет основание - причину (поле reason).
;;Если данный объект не является производным от другого
;;объекта statement, в поле reason устанавливается
;;значение 0.
;;В поле tag устанавливается уникальный числовой
;;идентификатор объекта - число, большее 0.
;;В поле DONE устанавливается одно из
;;следующих значений:
;;0 означает, что объект еще не обрабатывался;
;;1 означает, что объект обрабатывался в предположении
;;о правдивости высказывания;
;;2 означает, что объект обрабатывался в предположении
;;о лживости высказывания, (deftemplate statement
(field speaker (type SYMBOL))
(multifield claim (type SYMBOL))
(field scope (type SYMBOL) (default truth))
(multifield reason (type INTEGER) (default 0))
(field tag (type INTEGER) (default 0))
(field done (type INTEGER) (default 0))
)
;;Объект world представляет множество утверждений,
;;сформированных при определенном предположении
;;о правдивости или лживости высказывания,
;;принадлежащего некоторому персонажу.
;;Объект имеет уникальный идентификатор
;;в поле tag, который соответствует
;;тэгу высказывания.
;;Смысл допущения - истинность или лживость -
;;фиксируется в поле scope.
;;Поле TASK содержит одно из перечисленных
;;ниже значений:
CHECK - анализ предположений о
;;правдивости или лживости высказывания;
;;CONTRA - анализ обнаруженного противоречия;
;; CLEAN - удаляет все утверждения, созданные
;;в противоречивом "мире" ;
;;BАСК - откат в точку возврата;
QUIT - прекращение процесса.
;;Поле prior может содержать идентификатор
;;объекта world, обработанного перед тем,
;;как был создан данный объект, и с которым данный
;;объект может потенциально конфликтовать.
;;Поле upper содержит идентификатор другого объекта
;;world, в который внедрен данный объект, если
;;соответствующее высказывание содержит другое
;;высказывание.
;;Например, А говорит, что В сказал, что А - лжец.
;;В поле context сохраняется текущий контекст
;;анализируемого операнда дизъюнкции.
;;Поле done содержит информацию о том, обработано ли
;; уже высказывание, на основании которого создан этот
;; объект.
(deftemplate world
(field tag (type INTEGER) (default 1))
(field scope (type SYMBOL) (default truth))
(field task (type SYMBOL) (default check))
(field prior (type INTEGER) (default 0))
(field upper (type INTEGER) (default 0))
(field context (type INTEGER) (default 0))
(field done (type INTEGER) (default 0))
)
;; ФУНКЦИЙ
;; Изменяет область определения предиката с Т на F
;; и наоборот.
(deffunction flip (?P)
(if (eg ?P Т) then F else T)
)
;; ПРАВИЛА
Распаковка высказываний
;;ЕСЛИ объект world базируется на предположении о
;;правдивости высказывания,
;;ТО предположить, что персонаж говорит правду и что
;;высказывание истинно.
;;Значение поля TAG объекта statement передается в поле
;;reason объектов claim.
;;ПРИМЕЧАНИЕ. Это правило не используется для
;;распаковки метавысказываний. (defrule unwrap-true
?W <- (world (tag ?N) (scope truth) (task check)
(done 0)) ?S <- (statement (speaker ?X)
(claim ?PS:(not (eg ?P SAY)) $?Y) (done 0)) =>
(printout t crlf
"Assuming " T ?X " and " ?P $?Y " in world " ?N
;; "Предполагается " T ?X " and " ?P $?Y " в мире " ?N t crlf
)
;;Зафиксировать, что высказывание было распаковано
;;в предположении о его правдивости,
;;modify ?S (tag ?N) (done 1))
;;Зафиксировать в объекте world, что высказывание
;;распаковано, modify ?W (done 1))
;;Предположим, что персонаж в текущем "мире" является
;;правдолюбцем.
(assert (claim (content Т ?Х) (reason ?N)
(scope truth) ) )
;; Предполагается, что утверждение в высказывании
;; истинно. (assert (claim (content ?P $?Y) (reason ?N)
(scope truth)))
)
;; ЕСЛИ объект world базируется на предположении о
; ; правдивости метавысказывания,
;; ТО предположить , что персонаж говорит правду и что
;; высказывание истинно.
(defrule unwrap-true-state
?W <- (world (tag ?N) (scope truth) (task check)
(done 0)) ?S <- (statement (speaker ?X) (claim SAY ?Z $?Y)
(done 0)) =>
(printout t crlf "Assuming " T ?X " and " ?Z " says " $?Y
" in world " ?N
;; "Предполагается " T ?X " и " ?Z " говорит " $?Y
;; " в мире " ?N t crlf
)
;; Зафиксировать, что высказывание было распаковано
;; в предположении о его правдивости.
(modify ?S (tag ?N) (done 1))
; ; Предположим, что персонаж в текущем мире является
; ; правдолюбцем.
(assert (claim (content T ?X) (reason ?N)
(scope truth) ) )
;; Зафиксировать в объекте world, что высказывание
;; распаковано . (modify ?W (done 1))
; ; Сформировать новый объект world для внедренного
;; высказывания и зафиксировать, что этот объект
;; является внутренним по отношению к объекту
?N. (assert (world (tag (+ ?N 1)) (scope truth)
(upper ?N)))
;; Зафиксировать внедренное высказывание в новом
;; объекте world. (assert (statement (speaker ?Z) (claim $?Y)
(reason ?N)))
)
;; ЕСЛИ объект world базируется на предположении о
;; лживости высказывания,
;; ТО предположить, что персонаж лжет и что
;; высказывание ложно.
;; ПРИМЕЧАНИЕ. Это правило не используется для
;; распаковки метавысказываний. (defrule unwrap-false
?W <- (world (tag ?N) (scope falsity) (task check))
?S <- (statement (speaker ?X)
(claim ?P&:(not (or (eq ?P NOT) (eq ?P SAY))) $?Y)
(tag ?N) (done 1) ) =>
(printout t crlf
"Assuming " F ?X " and NOT " in world " ?N
;; "Предполагается " F ?X " и HE " ?P $?Y " в мире " ?N t crlf
)
;; Зафиксировать, что высказывание анализируется
;; в предположении о его лживости.
(modify ?S (scope falsity) (done 2))
;; Зафиксировать в объекте world, что анализируется
;; лживость высказывания.
(modify ?W (done 2))
;; Предположим, что персонаж лжец.
(assert (claim (content F ?X) (reason ?N)
(scope falsity)))
;; Сформировать отрицание утверждения,
(assert (claim (content NOT ?P $?Y) (reason ?N)
(scope falsity))) )
ПРИМЕЧАНИЕ. Правило используется только для работы с метавысказываниями, которые не содержат отрицаний. Правило не может обрабатывать метавысказываний вида: А: "В не говорил, что он лжец." или А: "В говорил, что он не лжец." (defrule unwrap-false-state
?W <- (world (tag ?N) (scope falsity)
(task check)) ?S <- (statement (speaker ?X)
(claim SAY ?Z $?Y)
(tag ?N) (done 1)) =>
(printout t crlf "Assuming " F " "?X "
and NOT " ?Z " says " $?Y
" in world " ?N
;; "Предполагается " F " "?X " и HE " ?Z " говорит
;; " $?Y " в мире " ?N t crlf
)
;; Изменить значение в поле scope текущего объекта
;; world.
(modify ?W (scope falsity) (done 2))
;; Зафиксировать, что высказывание было распаковано
;; в предположении о лживости,
(modify ?S (scope falsity) (done 2))
;; Предположить, что в текущем "мире" персонаж,
;; произнесший метавысказывание, лжец,
(assert (claim (content F ?X) (reason ?N)
(scope falsity))) )
;;-------------------------------
;; ЛОГИЧЕСКИЕ ОПЕРАТОРЫ
;; Правила отрицания
;; ЕСЛИ некто не правдолюбец,
;; ТО он лжец. (defrule notl
(declare (salience 5))
?F <- (claim (content NOT Т ?P)) =>
(modify ?F (content F ?P)) )
;; ЕСЛИ некто не лжец,
;; ТО он правдолюбец, (defrule not2
(declare (salience 5))
?F <- (claim (content NOT F ?P)) =>
(modify ?F (content Т ?Р)) )
;;---------------------
;; Распространение отрицания на дизъюнкцию,
(defrule not-or
(declare (salience 5))
?F <- (claim .(content NOT OR ?P ?X ?Q ?Y)) =>
(modify ?F (content AND (flip ?P) ?X (flip ?Q) ?Y))
)
;;-------------------------
;; Распространение отрицания на конъюнкцию,
(defrule not-and
(declare (salience 5))
?F <- (claim (content NOT AND ?P ?X ?Q ?Y)) =>
(modify ?F (content OR (flip ?P) ?X (flip ?Q) ?Y))
)
;;------------------------------
;; Устранение конъюнкции, (defrule conj
(world (tag ?N) (scope ?V) (task check)
(context ?L)) (claim (content AND ?P ?X ?Q ?Y) (reason ?N)
(scope ?V) (context ?L) =>
(assert (claim (content ?P ?X) (reason ?N)
(scope ?V) (context ?L))
(assert (claim (content ?Q ?Y) (reason ?N)
(scope ?V) (context ?L))
;; ОБРАБОТКА ДИЗЪЮНКТИВНЫХ УТВЕРЖДЕНИЙ
)
;; ЕСЛИ мы имеем дело с дизъюнктивным утверждением,
;; т.е. context = 0,
;; ТО сначала проанализировать левый дизъюнкт.
;; ПРИМЕЧАНИЕ. Устанавливается значение 1 как в поле
;; context объекта world, так и в поле context нового
; ; объекта claim.
(defrule left-disjunct
?W <- (world (tag ?N) (task check)
(scope ?V) (context 0))
(claim (content OR ?P ?X ?Q ?Y) (reason ?N)
(scope ?V) (context 0» =>
(assert (claim (content ?P ?X)
(reason ?N) (scope ?V) (context 1)))
(modify ?W (context 1))
)
;; ЕСЛИ при анализе левого дизъюнкта обнаружено
;; противоречие ,
;; ТО проанализировать правый дизъюнкт.
(defrule right-disjunct
(declare (salience 10))
?W <- (world (tag ?N) (task contra) (context 1))
(claim (content OR ?P ?X ?Q ?Y) (reason ?N)
(scope ?V)) =>
(assert (claim (content ?Q ?Y)
(reason ?N) (scope ?V) (context 2)))
(modify ?W (task check) (context 2))
)
;; ЕСЛИ выполнен откат к анализу правого дизъюнкта,
;; ТО установить соответствующий контекст.
(defrule resume-disjunct
?W <- (world (tag ?N) (task back) (context 1))
(claim (content OR ?P ?X ?Q ?Y) (reason ?N) (scope ?V))
=>
(assert (claim (content ?Q ?Y) (reason ?N)
(scope ?V) (context 2))) (modify ?W
(task check) (context 2))
)
;; ЕСЛИ анализ обоих дизъюнктов в предположении о
;; правдивости персонажа привел к противоречию
;; в том же самом "мире" ,
;; ТО выполнить анализ, предполагая, что персонаж лжет.
(defrule false-disjuncts
?W <- (world (tag ?M) (scope truth)
(task contra) (prior 0) (context 2))
(not (claim (reason ?M) (context 2))) =>
(modify ?W (scope falsity) (task check) (context 0))
)
;; ЕСЛИ анализ в предположении о правдивости персонажа
;; привел к противоречию с другим "миром" ,
;; ТО выполнить анализ, предполагая, что персонаж лжет.
(defrule other-world
?W <- (world (tag ?N) (scope truth) (task contra)
(prior ?M&"0) (context 0)) =>
(modify ?W (scope falsity) (task check))
)
;;ОБРАБОТКА ПРОТИВОРЕЧИЙ
;;ЕСЛИ обнаруживается противоречие между предположением
;;и производными от него фактами в пределах одного и
;;того же мира и в одном и том же контексте,
;;ТО зафиксировать противоречие и удалить
;;противоречивые утверждения (объекты claim)
;;из базы фактов, (defrule contradiction
(declare (salience 100))
?W <- (world (tag ?N) (task check) (scope ?V)
(context ?S)) ?P <- (claim
(content ?F ?X) (scope ?V) (reason ?N)
(context ?S)) ?Q <- (claim
(content ?G&:(-not (eq ?G ?F)) ?X)
(scope ?V) (reason ?N) (context ?S)) =>
(printout t crlf
"CONTRADICTION: " ?F ?X " versus " ?G ?X "in world " ?N
;; "ПРОТИВОРЕЧИЕ между: " ?F ?X " и "?G ?X "в мире " ?N
t crlf) (retract ?P) (retract ?Q)
(modify ?W (task contra))
;; ЕСЛИ обнаруживается противоречие между предположением
;; и производными от него фактами в пределах одного и
;; того же мира, но в разных контекстах,
;; ТО зафиксировать противоречие.
(defrule transcontext
(declare (salience 90))
?W <- (world (tag ?N) (task check) (scope ?V)
(context ?T))
(claim (content ?F ?X) (scope ?V) (reason ?N)
(context ?S&:(< ?S ?T)))
(claim (content ?G&:(not (eq ?G ?F)) ?x') (scope ?V)
(reason ?N) (context ?T)) =>
(printout t crlf
"TRANSCONTEXT CONTRADICTION: " ?F ?X " versus "
?G ?X " in world " ?N
;; "ТРАНСКОНТЕКСТНОЕ ПРОТИВОРЕЧИЕ между: "
?F ?X ;; " и "?G ?X "в мире " ?N
t crlf) (modify ?W (task contra))
)
;; ЕСЛИ обнаруживается противоречие между
;; текущим "миром" в предположении о правдивости
;; и ранее покинутым "миром",
;; ТО зафиксировать противоречие.
(defrule transworld-truth (declare (salience 80))
?W <- (world (tag ?N) (scope truth) (task check)
(upper 0))
;; В текущем "мире" имеется утверждение,
;; противоречащее утверждению в другом "мире",
(claim (content ?F ?X) (reason ?N))
;; "Мир", с которым обнаружен конфликт, имеет
;; идентификатор, меньший, чем текущий "мир",
;; т.е. сформирован раньше,
(claim (content ?G&:(not (eq ?G ?F)) ?X)
(reason ?M&:(< ?M ?N))) =>
(printout t crlf
"TRANSWORLD CONTRADICTION: " ?F ?X "
versus ?G ?X " in worlds " ?N "|" ?M
;; "МЕЖМИРОВОЕ ПРОТИВОРЕЧИЕ: " ?F ?X " противоречит
;; ?G ?X " в мирах " ?N "|" ?M
t crlf) (modify ?w (task contra))
;;ЕСЛИ обнаруживается противоречие между
;;текущим "миром" в предположении о лживости
;;и ранее покинутым "миром",
;;ТО подготовиться к выполнению отката в ранее
;;покинутый "мир". (defrule transworld-falsity
(declare (salience 80)) ?W <- (world (tag ?N)
(scope falsity)
(task check) (upper 0)) (claim (content ?F ?X)
(reason ?N)) (claim
(content ?G&:(not (eq ?6 ?F)) ?X) (reason ?M&:(< ?M ?N))) =>
(printout t crlf
"TRANSWORLD CONTRADICTION: " ?F ?X "
versus ?G ?X " in worlds " ?N "|" ?M
;; "МЕЖМИРОВОЕ ПРОТИВОРЕЧИЕ: " ?F ?X " противоречит
;; ?G ?X " в мирах " ?N "|" ?M
t crlf) (modify ?W (task contra) (prior ?M))
(defrule upper-world
(declare (salience 80))
?W <- (world (tag ?N)
(task check) (upper ?U&"0))
(claim (content ?F ?X) (reason ?N))
(claim
(content ?G&:(not (eq ?G ?F) ) ?X)
(reason ?M&:(< ?M ?N))) ?S <- (statement (tag ?N) (reason ?U)) =>
(printout t crlf
"TRANSWORLD CONTRADICTION: " ?F ?X "
versus " ?G ?X " in worlds " ?N "|" ?M
;; "МЕЖМИРОВОЕ ПРОТИВОРЕЧИЕ: " ?F ?X "
противоречит " ;; ?G ?X " в мирах " ?N "|" ?M
t crlf) (retract ?S) (modify ?W (task contra) (prior ?U))
;;ОПЕРАЦИИ УДАЛЕНИЯ
;; Удаление дизъюнкта, (defrule clean-context
(declare (salience 50)) (world
(tag ?N)
(task ?T&:(or (eg ?T contra) (eq ?T back))
(context ?S&~0))
?F <- (claim (reason ?N) (context ?S)) =>
(retract ?F)
;; ЕСЛИ текущий мир проанализирован только
;; в предположении о правдивости,
;; ТО проанализировать его, предполагая
;; лживость персонажа.
(defrule switch-scope
(declare (salience 40))
?W <- (world (tag ?N) (scope truth) (task contra)
(context ?C&~1) =>
(modify ?W (scope falsity) (task check))
)
;; Удалить все утверждения, сделанные в предположении
;; о правдивости, перед тем как анализировать
;; предположение о лживости, (defrule sweep-claims
(declare (salience 100))
(world
(tag ?N) (scope truth) (context ?C&~1)
(task ?T&:(or (eq ?T contra) (eq ?T back))))
?F <- (claim (reason ?N) (scope truth) (context ?D&~1)) =>
(retract ?F)
)
;; Удалить все объекты statement, основанные на предположении
;; о правдивости, перед тем как анализировать
;; предположение о лживости, (defrule sweep-statements
(declare (salience 100))
(world
(tag ?N) (task ?T&:(or (eq ?T contra)
(eq ?T back))) (scope truth) (context 0))
?F <- (statement (reason ?N) (scope truth)) =>
(retract ?F)
)
;; Удалить утверждения, связанные с "миром",
;; в котором обнаружены противоречия.
(defrule kill-claims
(declare (salience 100)) (world (tag ?N)
(task clean)) ?F <- (claim (reason ?N))
=>
(retract ?F)
)
;; ЕСЛИ все ненужные объекты claim или statement удалены,
;; ТО удалить объект world, которому назначена задача clean,
(defrule stop-killing
(declare (salience 100))
?W <- (world (tag ?N) (task clean))
(not (claim (reason ?N))) =>
(retract ?W)
)
;;ОПЕРАЦИИ ОТКАТА
(defrule undirected-falsity (declare (salience 20))
(world (tag ?N) (scope falsity)
(task contra)) ?W <- (world (tag ?M&:(< ?M ?N))
(scope truth) (task check)) =>
(modify ?W (task back))
;;Хронологический откат к тому "миру", который был
;;покинут без завершения анализа дизъюнктов,
(defrule undirected-disjunct (declare (salience 20))
(world (tag ?N) (scope falsity) (task contra))
?V <- (world (tag ?M&:(< ?M ?N)) (task check)
(context 1)) (claim (content OR ?P ?X ?Q ?Y) (reason ?M)
(scope ?S)) =>
;; Дизъюнкт в ранее покинутом "мире", анализ которого
;; не был выполнен.
(assert (claim (content ?Q ?Y) (reason ?M) (scope ?S)
(context 2));
;; Зафиксировать необходимость отката в этот "мир".
(modify ?V (task back))
;; Удаление объектов world .
;; ЕСЛИ выполняется откат к объекту М,
;; ТО удалить все объекты world,
;; имеющие идентификатор, больший М.
(defrule undo-world
(declare (salience 50)) (world (tag ?M)
(task back)) ?W <- (world (tag ?N&:(> ?N ?M))) ==>
(retract ?W)
)
;; Откат к прежним высказываниям.
(defrule restate
(declare (salience 50)) (world (tag ?M)
(task back)) ?S <- (statement (tag ?N&:
(> ?N ?M)] (reason 0) (done ?X&"0))
=>
(modify ?S (done 0))
)
;; Удаление объектов claim.
;; ЕСЛИ выполняется откат к объекту world M,
;; ТО удалить все объекты claim,
;; связанные с удаленными объектами world.
(defrule unclaim
(declare (salience 30))
(world (tag ?H) (task back))
?F <- (claim (reason ?N&:(> ?N ?M))) =>
(retract ?F)
)
;; Удаление объектов statement.
;; ЕСЛИ выполняется откат к объекту world M,
;; ТО удалить все объекты statement,
;; связанные с удаленными объектами world.
(defrule unstate
(declare (salience 30))
(world (tag ?M) (task back))
?F <- (statement (reason ?N&:(> ?N ?M))) =>
(retract ?F)
)
;;Возобновление процесса вычислений,
;;начиная с точки возврата.
;;ЕСЛИ все объекты world, созданные
;;после объекта М, удалены,
;;ТО повторно сформировать объект М,
;;предположив лживость высказывания.
(defrule restart
(declare (salience 20))
?W <- (world (tag ?M) (scope truth)
(task back) (context ?C&~1)) =>
(modify ?W (scope falsity) (task check) (context 0))
)
;;ПЕРЕХОД К АНАЛИЗУ СЛЕДУЮЩЕГО "МИРА" И
;;ВЫВОД ОТЧЕТА О РЕЗУЛЬТАТАХ
;;Переход к анализу следующего "мира",
;;ЕСЛИ никакие другие правила не ожидают активизации,
;;ТО анализ текущего "мира" завершен и
;;можно приступить к формированию нового "мира",
;;если имеются необработанные высказывания.
;;ПРИМЕЧАНИЕ. Это правило имеет приоритет,
;;более низкий, чем все прочие правила,
;;исключая правило вывода результатов,
(defrule move
(declare (salience -50))
;;Существует "мир", сформированный на основе
;;исходного высказывания.
?W <- (world (tag ?N&:(> ?N 0)) (task check))
;;В базе фактов отсутствуют объекты world,
;;созданные позже текущего.
(not (world (tag ?T&:(> ?T ?N))))
В базе фактов имеется высказывание, подготовленное к созданию нового объекта world.
(statement (reason 0) (done 0)) =>
;; Сформировать новый объект world на основе
;; этого объекта statement.
(assert (world (tag (+ ?N 1))))
)
;;ЕСЛИ отсутствуют противоречия в объектах world,
;;ТО распечатать результаты.
;;ПРИМЕЧАНИЕ. Это правило будет активизироваться
;;повторно до тех пор, пока не будет выведена
;;непротиворечивая интерпретация,
(defrule report-results
(declare (salience -40)) (not (world (task contra)))
(not (statement (reason 0) (done 0)))
(statement (tag ?N) (done ?MS~0))
(claim (content ?P ?X) (reason ?N)) =>
(printout t crlf
"RESULT: " ?P ?X " from statement " ?N
;; "РЕЗУЛЬТАТ: " ?P ?X " из высказывания " ?N
t crlf)
;; ЕСЛИ противоречие остается и после анализа всех точек отката
;; и нет больше правил, которые можно было бы активизировать,
;; ТО прекратить процесс вычислений,
(defrule sanity-check
(declare (salience -100))
(world (tag ?N) (task ?T&:(or (eg ?T contra)
(eq ?T back))))
(not (world (tag ?M&:(< ?M ?N))
(scope truth) (task check))) =>
(printout t crlf
"FAIL: Statements inconsistent, detected in world " ?N
;; "РЕШЕНИЕ НЕ НАЙДЕНО: Высказывания противоречивы,
;; обнаружены в мире " ?N
t crlf) (halt)
)
методику прямого логического вывода, которая обеспечивает разрешение конфликтов (глава 5);
целенаправленный логический вывод с использованием лексем задач (главы 5 и 14);
анализ множества контекстов при разных исходных предположениях (главы 17 и 19).
На примере этой программы вы также могли убедиться в том, что несмотря на модульную структуру, расширение ее функциональных возможностей сопряжено с определенной модификацией ранее разработанных модулей (правил), которые должны учитывать изменения, вносимые в структуру данных.
А.4. Задача "Правдолюбцы и лжецы"
А.4. Задача "Правдолюбцы и лжецы"
Для того чтобы продемонстрировать вам возможности языка CLIPS, я выбрал головоломку, а не задачу из практики применения экспертных систем. В головоломке решается одна из задач, возникающих на острове, населенном обитателями двух категорий: одни всегда говорят правду (назовем их правдолюбцами), а другие всегда лгут (их, естественно, назовем лжецами). Множество подобных головоломок вы можете встретить на страницах занимательной книги Раймонда Смуляна (Raymond Smullyan) What is the Name of this Book?. Ниже приведены разные задачи из этой серии.
Р1. Встречаются два человека, А и В, один из который правдолюбец, а другой — лжец. А говорит: "Либо я лжец, либо В правдолюбец". Кто из этих двоих правдолюбец, а кто лжец?
Р2. Встречаются три человека, А, В и С. А и говорит: "Все мы лжецы", а В отвечает: "Только один из нас правдолюбец". Кто из этих троих правдолюбец, а кто лжец?
РЗ. Встречаются три человека, А, В и С. Четвертый, проходя мимо, спрашивает А: "Сколько правдолюбцев среди вас?" А отвечает неопределенно, а В отвечает: "А сказал, что среди нас есть один правдолюбец". Тут в разговор вступает С и добавляет: "В врет!" Кем, по-вашему, являются В и С?
В программе, решающей проблемы подобного класса, будут использованы широкие возможности средств программирования правил в языке CLIPS и продемонстрированы некоторые интересные приемы, например использование контекстов и обратного прослеживания. Мы также покажем, как конструировать и тестировать прототипы, которые приблизительно воспроизводят поведение окончательной программы. Как отмечалось в основном материале книги, технология построения экспертных систем с использованием прототипов — одна из самых распространенных в настоящее время.
А.5. Стиль программирования на языке CLIPS
А.5. Стиль программирования на языке CLIPS
В главе 17 был представлен обзор инструментальных средств конструирования экспертных систем и ряд рекомендаций, касающихся методики их проектирования. Многие из описанных в той главе средств имеют функциональные возможности, весьма близкие к тем, которые вы можете найти в CLIPS. Большинство рекомендаций, относящихся к методике проектирования систем, основанных на правилах, сохраняют свою силу и при использовании в качестве основного инструмента проектирования языка CLIPS. В частности, работая с CLIPS, нужно стараться так организовать систему правил, чтобы каждое из них было как можно проще. Я бы рекомендовал вновь перечитать главу 17 после того, как вы внимательно проанализируете описанную в этом Приложении программу.
Эта программа является относительно простой и включает всего 35 правил, тогда как в практических экспертных системах их может быть значительно больше. Например, в прототипе системы R1/XCON, который был разработан в 1980 году (см. главу 14), содержалось около 750 правил, причем по мере совершенствования системы их число росло и к 1984 году достигло 3300. В среднем каждое правило в R1 анализирует шесть условий и выполняет три действия.
Как и при программировании любых других задач, ключевым условием разработки "хорошего" программного кода является правильный выбор набора абстрактных понятий, которыми должна манипулировать программа, и набора операций, которые она должна выполнять. Первое условие позволит рационально выбрать структуру объектов и форму представления условий в левой части правил, а второе — рационально организовать действия в правой части. Как было показано в разделе A3, использование объектов и обработчиков сообщений позволяет успешно решить задачу рациональной организации данных и процедур в программе.
я предлагаю рассмотреть другой вырожденный
Листинг А.1. Трассировка решения задачи Р0
CLIPS>
(reset)
==>
f-0 (initial-fact)
==>
f-1 (world (tag 1) (scope truth))
==>
f-2 (statement (speaker A)
(claim F A) (reason 0) (tag 1))
CLIPS>
(run)
FIRE 1 unwrap-true: f-1,f-2
Assumption:
A is a knight, so (T A) is true.
==>
f-3 (claim (content F A) (reason 1)
(scope truth))
==>
f-4 (claim (content T A) (reason 1)
(scope truth)) FIRE 2 contra-truth:
f-1, f-2, f-4, f-3
Statement is inconsistent if A is a knight.
<== f-3 (claim (content F A) (reason 1)
(scope truth)) <== f-4 (claim (content T A)
(reason 1) (scope truth)) <== f-1 (world (tag 1)
(scope truth)) ==>
f-5 (world (tag 1)
(scope falsity)) FIRE 3 unwrap-false:
f-5, f-2 Assumption
A is a knave, so (T A) is false.
==>
f-6 (claim (content NOT F A)
(reason 1) (scope falsity))
==>
f-7 (claim (content F A) (reason 1) (scope falsity))
FIRE 4 not2: f-6
<== f-6 (claim (content NOT F A) (reason 1) (scope falsity))
==>
f-8 (claim (content Т A) (reason 1) (scope falsity))
FIRE 5 contra-falsity: f-5, f-2, f-7, f-8
Statement is inconsistent if A is a knave.
<== f-5 (world (tag 1) (scope falsity))
==>
f-9 (world (tag 1) (scope contra))
Упражнение 1
Читателям, желающим самостоятельно поэкспериментировать с этой программой, я предлагаю рассмотреть другой вырожденный случай головоломок этого класса.
Предположим, что персонаж А утверждает: "Я всегда говорю правду". К какой категории следует отнести этот персонаж?
В такой постановке задача имеет неоднозначное решение. Предположение, что А правдолюбец, не приводит нас к противоречию. Но точно так же не приводит к противоречию и предположение, что А —лжец.
Ваша задача— модифицировать описанную выше программу таким образом, чтобы она давала заключение, что оба контекста непротиворечивы. Один из возможных вариантов модификации — ввести в состав программы правила consist-truth и consist-falsity, разработав их по образцу правил contra-truth и contra-falsity. Эти правила должны дать пользователю знать, что при данном предположении противоречий не обнаружено, причем правила должны активизироваться в случае, когда нет больше правил, претендующих на внимание интерпретатора.
Обратите внимание на то, что в задачах этого класса недостаточно убедиться, что начальное предположение об истинности некоторого высказывания не приводит к противоречию. Необходимо еще и проверить, приведет ли к противоречию обратное предположение. Если окажется, что оно также непротиворечиво, значит, задача не имеет единственного решения.
Трассировка решения задачи
Листинг А.2. Трассировка решения задачи Р4
CLIPS>
(reset)
==>
f-0 (initial-fact)
==>
f-1 (world (tag 1) (scope truth) (context 0))
==>
f-2 (statement (speaker A)
(claim OR F A T B) (reason 0) (tag 1))
CLIPS>
(run)
FIRE 1 unwrap-trues f-1,f-2
Assumption
A is a knight, so (OR F A T B) is true.
==>
f-3 (claim (content OR F A T B)
(reason 1) (scope truth) (context 0))
==>
f-4 (claim (content T A) (reason 1)
(scope truth) (context 0)) FIRE 2 left-or: f-1,f-3
==>
f-5 (claim (content F A) (reason 1)
(scope truth) (context 1))
<== f-1 (world (tag 1) (scope truth) (context 0))
==>
f-6 (world (tag 1) (scope truth) (context 1))
FIRE 3 contra-truth-scope: f-6,f-4,f-5
Disjunct 1 is inconsistent with earlier truth context.
<== f-5 (claim (content F A) (reason 1)
(scope truth) (context 1)) FIRE 4 right-or: f-6,f-3 .
==>
f-7 (claim (content Т В) (reason 1)
(scope truth) (context 2))
<== f-6 (world (tag 1) (scope truth)
(context 1))
==>
f-8 (world (tag 1) (scope truth)
(context 2))
FIRE 5 consist-truth: f-8, f-2
Statement is consistent:
<== f-8 (world (tag 1) (scope truth) (context 2))
==>
f-9 (world (tag 1) (scope consist) (context 2))
FIRE 6 true-knight: f-9, f-7
В is a knight
<== f-7 (claim (content Т В) (reason 1)
(scope truth) (context 2))
FIRE 7 true-knight: f-9,f-4
A is a knight
<== f-4 (claim (content Т A) (reason 1)
(scope truth) (context 0))
CLIPS>
Программирование на языке CLIPS
ПРИЛОЖЕНИЕ. Программирование на языке CLIPS А.1. Краткая история CLIPS А.2. Правила и функции в CLIPS А.2. Правила и функции в CLIPS А.2.1. Факты А.2.2. Правила А.2.3. Наблюдение за процессом интерпретации А.2.4. Использование шаблонов А.2.5. Определение функций А.3. Объектно-ориентированные средства в CLIPS А.4. Задача "Правдолюбцы и лжецы" А.4.1. Анализ проблемы А.4.2. Онтологический анализ и представление знаний А.4.3. Разработка правил А.4.4. Расширение набора правил — работа с составными высказываниями А.4.5. Обратное прослеживание и множество контекстов А.4.6. Обработка метавысказываний А.4.7. Полный листинг программы А.5. Стиль программирования на языке CLIPS Упражнения
Программирование на языке CLIPS
Программирование на языке CLIPS
А.1. Краткая история CLIPS
А.2. Правила и функции в CLIPS
А.З. Объектно-ориентированные средства в CLIPS А.4. Задача "Правдолюбцы и лжецы"
А.5. Стиль программирования на языке CLIPS Упражнения
Разработайте программу моделирования, которая использовала
Упражнения
1. Разработайте программу моделирования, которая использовала бы объекты и правила, аналогичные описанным в разделе А.З, но для какого-нибудь другого устройства.
2. Разработайте программу, которая решала бы детективные головоломки типа той, что представлена ниже (головоломки описаны в книге [Smullyan, 1978]).
Расследуется дело об ограблении, причем подозреваемых трое — А, В и С. По крайней мере один из них виновен и известно, что никто, кроме этих троих, не мог принимать участия в ограблении.
Задача 1. А никогда не работает в одиночку, т.е. имеет хотя бы одного сообщника. У С есть твердое алиби. Виновен или невиновен В?
Задача 2. А никогда не работает с С. С никогда не работает в одиночку. Если А виновен, а В невиновен, то С виновен. Кто из этих троих совершил ограбление?
Задача 3. Если А виновен, то у него есть точно один сообщник. Если точно два виновны, то А — один из них. В и С дают показания, из которых следует, что у другого из них есть алиби. Кто совершил ограбление?
Программа должна решать все три задачи.
3. Критически проанализируйте и модифицируйте любую CLIPS-программу, представленную на страницах данной книги.