Расширение набора правил – работа с составными высказываниями
Расширим теперь возможности программы таким образом, чтобы она могла работать с составными высказываниями. Это даст возможность охватить в ней не только вырожденный случай, рассмотренный в предыдущем разделе, но и более сложные. За основу возьмем следующую головоломку.
Р4. Встречаются два персонажа, А и В, каждый из которых либо лжец, либо правдолюбец. Персонаж А говорит: "Мы оба лжецы". К какой категории следует отнести каждого из них?
В этой задаче нам придется иметь дело с конъюнкцией, поскольку утверждение, высказанное персонажем А, моделируется выражением.
Эту конъюнкцию нужно разделить на выражения-компоненты и проанализировать их непротиворечивость. Очевидно, что А не может быть правдолюбцем, поскольку это противоречит утверждению, которое содержится в его реплике. Но программа должна самостоятельно "распаковать" эту конъюнкция для того, чтобы прийти к такому выводу.
Нам также понадобится снабдить программу и средствами обработки дизъюнкции, поскольку, если предположить, что А лжет, нужно будет оперировать с отрицанием этого утверждения, которое преобразует выражение:
Т(А) v Т(B).
Таким образом, в программу нужно включить правило выполнения отрицания составных высказываний и правило, которое "понимало" бы, что дизъюнкты вроде Т(А) в действительности являются предположениями. Составное выражение Т(А) v T(B) будем обрабатывать, предположив Т(А), и проанализируем, нет ли в нем противоречия. Если таковое не обнаружится, то можно предположить, что Т(А) v Т(B) совместимо с утверждением о том, что А лгун, т.е. F(A). Но если предположение Т(А) приведет к несовместимости, то нужно отказаться от него и предположить Т(Е). Если и это предположение приведет к несовместимости, то это означает, что утверждение Т(А) v T(B) несовместимо с предположением F(A). В противном случае Т(В) образует часть совместимой интерпретации исходного высказывания.
В CLIPS составные высказывания проще всего представлять с помощью так называемой "польской" (или префиксной) нотации операций. Суть этого способа представления операций состоит в том, что символ операции предшествует символам операндов. Каждый оператор имеет фиксированное количество операндов, а потому всегда существует возможность однозначно установить область действия операции даже в случае, если операнды представляют собой вложенные выражения. Таким образом, выражение, представленное скобочной формой – (F(/4) ^ Т(В)), в польской записи будет иметь вид:
NOT AND F А Т В.
Легче всего восстановить исходный вид выражения, представленного в польской нотации, просматривая его справа налево. При этом операнды считываются до тех пор, пока не встретится объединяющий их оператор. Полученное выражение оказывается операндом следующего оператора. В представленном выше выражении В является операндом одноместного оператора Т, а пара операндов Т(В) и F(A) объединяется оператором AND.
Задавшись таким способом представления составных высказываний, сформируем правило выполнения отрицания дизъюнктивной и конъюнктивной форм, в котором будет использоваться функция 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)))
Использование функции flip упрощает преобразование и позволяет перейти от выражения:
NOT AND F А Т В
Прямо к:
OR Т A F В,
Минуя:
OR NOT F A NOT Т В.
Функция flip определена следующим образом:
(def function flip (?P) (if (eq?P Т) then F else T))