дб. Четвертое издание джозеф Джарратано Университет Хьюстон клиэрЛэйк Гари Райли People5oft, Издательский дом "Вильямс" Москва СанктПетербург Киев 2007 ббк 32. 973. 26 018 75 Д
Скачать 3.73 Mb.
|
Программа, модифицированная таким образом, приведена ниже. (defrule sum-rectangles (rectangle (height ?height) (width ?CLIPS> (watch facts)J CLIPS> (watch activations)J CLIPS> (reset)J > f — 0 (initial-fact) > f-1 (rectangle (height 10) (width б) > Activation 0 sum-rectangles: f-1 8.4. Суммирование значений с использованием правил 637 — > f-2 (rectangle (height 7) (width 5)) > Activation Об О sum-rectangles: f- 3 > f-4 (rectangle (height 2) (width 5)) > Activation О sum-rectangles: f-4 > f-5 (sum 0) CLIPS> (run)J FIRE 1 sum-rectangles: f-4 > f-б (асЫ-to-sum 10) > Activation Об б < — f-5 (sum 0) < — б (асЫ-to-sum 10) > f-7 (sum 10) FIRE 3 sum-rectangles: f-3 > f-8 (add-to-sum 48) > Activation О f-7,f-8 FIRE 4 sum-areas: f-7,f-8 < — f-7 (sum 10) < — f- 8 (add — to-sum 48) > f — 9 (sum 58) FIRE 5 sum — й > f-10 (add-to-sum 35) > Activation Об f-1 > f-12 (add-to-sum 60) > Activation О sum-areas: f-11,f-12 FIRE 8 sum-areas: f-11,f-12 < — f- 11 (sum 93) < — f-12 (add-to-sum 60) > f-13 (sum 153) CLIPS> (unlatch all)J Глава 8. Развитые средства сопоставления с шаблонами Правило sum — rectangles активизируется четыре раза, в то время как происходит вставка фактов rectangle в список фактов в результате выполнения команды reset. Правило sum-rectangles при каждом запуске обеспечивает ввод в список фактов такого факта, который активизирует правило sum-areas. Правило sum- areas обеспечивает добавление текущего значения площади к промежуточной сумме и удаление факта add-to — Поскольку шаблон правила sum — rectangles не сопоставляется с фактом sum, после ввода в список фактов нового факта sum Для связывания значения переменной со значением некоторого выражения может применяться функция bind. Функция bind имеет следующий синтаксис (bind вычисление которого приводит к получению либо однозначного, либо многозначного значения. Например, ниже приведено правило sum — areas, позволяющее выводить на внешнее устройство общую сумму и площади каждого прямоугольника, из которых складывалась эта сумма. (defrule sum-areas ?sum <- (sum ?total) ?new-area <- (асЫ-to-sum ?area) => (retract ?sum ? new-area) (printout t "Adding " ?area " to " ?total crlf) (printout t "New sum is " (+ ?total ?area) crlf) (assert (sum (+ ?total ?area)))) (defrule sum-areas ?sum <- (sum ?total) Обратите внимание на то, что в правой части этого правила выражение (+ ? total ? использовалось дважды. А замена двух отдельных вычислений с помощью одной функции bind позволяет устранить ненужные вычисления. Ниже приведено тоже правило, но сформулированное иначе благодаря использованию функции bind. 639 8.6. Функции ввода-вывода ?new-area <- (асЫ-to-sum ?area) — > (retract ?sum ?new-area) (printout t "Adding " ?area " to " ?total crlf) (bind ?new-total (+ ?total ?area)) (printout t "New sum is " ?new- total crlf) (assert (sum ?new-total))) Функция bind может применяться не только для создания новых переменных, предназначенных для включения в правую часть какого-либо правила, но и для повторного связывания со значением такой переменной, которая встречается в левой части некоторого правила. б Функции ввода-вывода Функция read В процессе работы экспертных систем часто возникает необходимость в том, чтобы пользователь ввел в программу некоторые данные. Система CLIPS позволяет считывать информацию с клавиатуры с использованием функции read. Основной синтаксис функции read не требует параметров, а следующий пример показывает, как используется эта функция для ввода данных CLIPS> (clear)J CLIPS> (defrule get-first-name е (printout t "What is your first name7 ") (bind ?response (read)) (assert (паве ?response)))J CLIPS> (reset)J CLIPS> (run)J What is your first name? GaryJ CLIPS> (facts)J f — О (initial-fact) f — 1 (пате Gary) For а total of 2 facts. CLIPS> Следует отметить, что функция read может считать введенную лексему только после нажатия клавиши ввода. Функция read может использоваться для ввода одновременно только одного поля. Все дополнительные знаки, введенные после ввода первого поля, вплоть до обозначения конца строки, сформированного путем нажатия клавиши ввода, отбрасываются. Например, если с помощью правила Глава 8. Развитые средства сопоставления с шаблонами 640 get-first — пате будет предпринята попытка прочитать с помощью следующего ввода не только имя, но и фамилию, то будет введено лишь первое поле, Gary: Gary RileyJ Функция open Язык CLIPS обеспечивает не только ввод с клавиатуры и вывод на терминал, но и позволяет читать данные из файлов и записывать в файлы. Но прежде чем появится возможность получить доступ к файлу для чтения или записи, файл необходимо открыть с помощью функции open. Количество файлов, которые могут быть открыты одновременно, зависит от конкретной операционной системы и применяемых в ней аппаратных средств. Функция open имеет следующий синтаксис представляющая имя файла на локальном компьютере. В данном примере используется файл с именем "input. dat". Имя файла может также включать информацию о пути (о каталоге, в котором находится файл. Формат спецификации пути, как правило, зависит от операционной системы, поэтому, чтобы правильно задать путь, необходимо иметь определенное представление об особенностях операционной системы применяемого компьютера. Чтобы обеспечить чтение всего ввода, необходимо заключить оба поля в двойные кавычки. Безусловно, после обозначения вводимых данных с помощью двойных кавычек эти данные преобразуются в единственное литеральное поле, поэтому исключается возможность легко получить доступ к отдельным полями. Функция read позволяет также вводить поляне являющиеся символами, строками, целочисленными значениями или числовыми значениями с плавающей точкой. В частности, с ее помощью можно вводить знаки круглых скобок. Такие поля обозначаются двойными кавычками и рассматриваются как строки. Указанная возможность демонстрируется с помощью следующего диалога в командной строке CLIPS> (read)J (J и(и CLIPS> 641 8.6. Функции ввода-вывода Второй параметр, функции open, а в дальнейшем ссылка на файл осуществляется только по его логическому имени, поэтому для обеспечения чтения из другого файла достаточно внести изменения лишь в вызов функции open. Третий параметр, Доступ для чтения и записи Доступ только для дополнения Если в качестве параметра не задано значение режима доступа к файлу < f i leaccess>, используется заданное по умолчанию значение "r". В некоторых операционных системах те или иные режимы доступа могут оказаться неприменимыми. Но большинство операционных систем поддерживает доступ для чтения (обеспечивающий ввод данных) и доступ для записи (обеспечивающий вывод данных. Доступ для чтения или записи, а также доступ для дополнения (позволяющий добавить выводимые данные к концу файла) может не всегда оказаться применимым. Важно помнить, что в разных операционных системах открытие файла может приводить к разным последствиям. Например, в операционных системах, не поддерживающие возможность создания многочисленных версий файла, таких как MS-DOS (на персональном компьютере) или Unix, при использовании режима доступа для записи при открытии файла заменяют существующий файл вновь созданным файлом. В отличие от этого, при открытии файла с применением Глава 8. Развитые средства сопоставления с шаблонами Функция C1OSe Если доступ к файлу больше не требуется, файл должен быть закрыт. Если такой файл не будет закрыт, тонет никакой гарантии, что записанная в него информация сохранится. Более того, чем дольше файл остается открытым, тем больше вероятность того, что из-за отключения питания или другого нарушения в работе не удастся сохранить записанную информацию. Функция close имеет следующую общую форму, в которой необязательный параметр (close data) А приведенные ниже операторы закрывают файлы, связанные с логическими именами input и output. (close input) (close output) Следует отметить, что для закрытия только конкретных файлов необходимо применять отдельные операторы. При использовании файлов важно помнить, что каждый открытый файл должен быть в конечном итоге закрыт с помощью функции close. Особенно важно то, что при отсутствии команды закрытия файла могут быть потеряны записанные в него данные. Система CLIPS не выводит приглашение для пользователя, чтобы он закрыл открытый файл. Единственная мера предосторожности, встроенная в систему CLIPS и касающаяся закрытия файлов, которые непреднамеренно были режима доступа для записи в операционной системе VMS на компьютере VAX создается новая версия файла, если файл уже существует, а старая версия файла не уничтожается. Функции open действует как предикативная функция (определение понятия предикативной функции приведено в разделе 8.8). Эта функция возвращает символ TRUE, если файл был открыт успешно в противном случае возвращается символ Возвращаемое значение может использоваться для проверки на наличие ошибок. Например, если пользователь задал имя файла, который не существует, и была предпринята попытка открыть файл с применением режима доступа для чтения, то функция open возвращает символ FALSE. Такой результат может быть проверен в правиле, где происходит открытие файла, после чего предприняты соответствующие действия, например, передано приглашение пользователю для ввода другого имени файла 8.6. Функции ввода-вывода 643 оставлены открытыми, состоит в том, что все открытые файлы закрываются после вызова на выполнение команды exit. Чтение из файла и запись в файл В примерах, которые рассматривались до сих пор в данной главе, все входные данные считывались с клавиатуры, а все выходные данные записывались на терминал. С другой стороны, с применением логических имен обеспечивается вводи вывод данных с использованием других источников данных. В главе было показано, что функция printout, в вызове которой задано логическое имя t, позволяет передавать выходные данные на экран. В функции printout могут также применяться другие логические имена для передачи выходных данных вместо назначения, отличное от экрана. При использовании логического имени t в качестве параметра с обозначением логического имени в любой функции вывода происходит запись выходных данных в стандартное устройство вывода, обычно на терминал. Аналогичным образом, при использовании логического имени t в качестве параметра с обозначением логического имени в любой функции ввода осуществляется ввод данных из стандартного устройства ввода данных, обычно с клавиатуры. Ниже приведен пример, который показывает, как применяются логические имена для записи в файл. CLIPS> (open "example.dat" example "w")J TRUE CLIPS> (printout example "green" crlf)J CLIPS> (printout example 7 crlf)J CLIPS> (close example)J TRUE CLIPS> (read [ Глава 8. Развитые средства сопоставления с шаблонами Вначале происходит открытие файла "example. dat", нона этот разв режиме доступа для чтения. Обратите внимание на то, что при открытии файла "example. dat" во втором вызове функции open не требовалось явно задавать опцию "r", поскольку она применяется по умолчанию. После открытия файла для выборки значений green и 7 из файла, связанного с логическим именем example, использовалась функция read. Обратите внимание на то, что после третьего вызова функции read возвращен символ. Система CLIPS возвращает это значение после вызова функций ввода данных, если предпринимаются попытки чтения вслед за концом файла. Проверяя это возвращаемое значение функции read (или другой функции ввода данных, можно определить тот момент, когда в файле уже не остается данных. Функция fOrmat Иногда возникает необходимость отформатировать вывод программы CLIPS, например, при оформлении данные в виде таблиц. Безусловно, для этой цели можно воспользоваться функцией printout, но имеется отдельная функция, специально предназначенная для форматирования и называемая format, которая предоставляет возможность воспользоваться широким разнообразием стилей форматирования. Функция format имеет следующий синтаксис *) По умолчанию функция read, если в ней не заданы параметры вызова, осуществляет чтение из стандартного устройства ввода данных, t. Это же логическое имя использовалось по умолчанию ив предыдущем примере вызова функции read. А в следующем примере показано, как можно применить логическое имя в функции read для выборки значений из файла "example. dat": CLIPS> (open "example.dat" example "r")J TRUE CLIPS> (read example)J green CLIPS> (read example)J 7 CLIPS> (read example)J EOF CLIPS> (close example)J TRUE CLIPS> 8.6. Функции ввода-вывода 645 Вызов функции format состоит из нескольких частей. Параметр Управляющая строка состоит из флажков формата, которые показывают, как должен осуществляться вывод данных, обозначенных параметрами функции format. Вслед за управляющей строкой находится список параметров. Количество флажков формата в управляющей строке определяет количество задаваемых параметров. В качестве параметров могут применяться либо значения констант, либо выражения. Возвращаемым значением функции format является отформатированная строка. Если в вызове команды format используется логическое имя nil, то вывод каких-либо выходных данных не производится (ни на терминал, нив файл, но возврат отформатированной строки все равно выполняется. Пример применения функции format показан в приведенном ниже диалоге, в котором создается отформатированная строка, содержащая имя некоторого лица (причем для этого имени резервируются 15 пробелов, аза этим именем следуют данные о возрасте этого лица. В данном примере заслуживает внимания то, как значения имении возраста выравниваются в своих столбцах. Таким образом функция format является удобным средством размещения данных по столбцам CLIPS> (format nil "Name: %-15s Age: %3d" "Bob Green" 35)J "Name: ВоЬ Green Age: 35" CLIPS> (format nil "Name: %-15s Age: %3d" "Ralph Heiden" 32) 1 "Name: Ralph Heiden Age: 32" CLIPS> Флажки формата всегда начинаются со знака процента, о. В управляющую строку можно также помещать обычные строковые данные, такие как "Name: ", для последующего вывода. Но некоторые флажки формата не форматируют параметры. Например, флажок "оп" используется для вывода знаков возврата каретки и (или) перевода строки„аналогично тому, как в команде printout применяется символ crl f. В данном примере для вывода имени в столбец, имеющий ширину знаков, используется флажок формата "о — 15s". Знак показывает, что вывод должен быть выровнен влево, а буква s говорит о том, что должна быть выведена строка или символ. Флажок формата "' 3d ' показывает, что число должно быть выведено как целочисленное значение, выровненное вправо в столбце шириной три знака. А если бы в качестве параметра для этого флажка формата было передано значение Глава 8. Развитые средства сопоставления с шаблонами 646 о М. Nx По умолчанию применяется выравнивание вправо. Выравнивание происходит, если количество знаковых позиций, предусмотренных для вывода значения, превышает количество знаков, необходимых для вывода этого значения. Если возникает такая ситуация, то выравнивание значения влево вызывает вывод указанного значения в левой части отведенного для него места, при условии, что неиспользуемое пространство в правой части заполняется пробелами. Применение выравнивания вправо приводит к тому, что значение выводится в правой части отведенного для него места, а пробелами заполняется неиспользуемое пространство слева. Буквой М в этой спецификации обозначается ширина поля в знаках. Если значение М задано, то выводится по меньшей мере такое количество знаков, которое задано числом М. Обычно для заполнения неиспользуемой части пространства вывода, состоящего из М знаков, применяются пробелы, если число Мне начинается с цифры О в последнем случае для заполнения служат ноли. Если выводимое значение превышает по ширине значение М, то функция format расширяет поле вывода настолько, насколько потребуется. Буквой N в спецификации формата задается необязательное число, определяющее количество цифр после десятичной точки, которое должно быть выведено. По умолчанию при выводе чисел с плавающей точкой после десятичной точки выводится шесть цифр. Буквах в спецификации представляет собой буквенное значение определяющее спецификацию формата отображения. Возможные значения спецификации формата отображения показаны в табл. 8.3. Функция readline Функция readline может использоваться для чтения полной строки ввода и имеет следующий синтаксис (readline [ Флажок формата имеет следующую общую спецификацию, в которой знак "—" является необязательными означает, что должно быть выполнено выравнивание влево. Функции ввода-вывода 647 Таблица 8.3. Спецификации формата отображения Знак Значение Целое число Число с плавающей точкой Число в экспоненциальном формате (в котором задаются значащая часть числа и его порядок степень числа десять) Общий (числовой) формат допускается отображение в любом формате, требующем меньшего количества знаков Восьмеричный число без знака (применение спецификатора N не допускается) Шестнадцатеричный формат; число без знака (применение спецификатора N не допускается) Строка; перед преобразованием строк, заключенных в кавычки, во внутреннее представление открывающая и закрывающая кавычки удаляются Знаки возврата каретки и (или) перевода строки Знак Ъ в своем непосредственном виде (используется логическое имя t, то входные данные считываются со стандартного устройства ввода данных. Функция readl inc возвращает в качестве строкового значения следующую строку ввода, полученную из источника входных данных, связанного с указанным логическим именем строка содержит все введенные данные, вплоть до обозначения конца строки, и включая это обозначение. А если достигнут конец файла, функция readline возвращает символ EOF. Такая ситуация может возникнуть только в том случае, если логическое имя, применяемое в функции readline, связано с файлом. Приведенный ниже диалог иллюстрирует использование функции readline. CLIPS> (clear)J CLIPS> (defrule get-name — > (printout t "What is your name? ") (bind ?response (readline)) (assert (пате ?response))) CLIPS> (reset)J CLIPS> (run)J What is your name? Gary RileyJ CLIPS> (facts)J f — О Глава 8. Развитые средства сопоставления с шаблонами 648 f — 1 (пате "Gary Riley" ) For а total of 2 facts. CLIPS> CLIPS> (defrule пате ;> (printout t "What is your паве ") (bind ? response (ехр1обе$ (readline))) (аззех. (паве ?response)))J CLIPS> (reset)J CLIPS> (run)J What is your name? Gary RileyJ CLIPS> (facts)J f — О (initial-fact) f — 1 (user's— - name Gary Riley) For а total of 2 facts. CLIPS>$ Перечень других функций, которые могут применяться для создания и манипулирования строками и многозначными значениями, приведен в приложении Д. Дополнительные сведения обо всех функциях, доступных в языке CLIPS, можно найти в документе Basic Pvogvamming Guide на компакт-диске, прилагаемом к данной книге. 8.7 Игра в палочки В следующих нескольких разделах для демонстрации различных методов управления процессом выполнения программы на языке, основанном на правилах, будет использоваться простая игра с двумя игроками, называемая (Игра в палочки. Цель игры Sticks состоит в том, чтобы избежать необходимости взять последнюю палочку из кучи палочек. Каждый игрок должен брать из кучи по В этом примере имя "Gary Riley" сохраняется в факте user' s — пате как единственное поле. А поскольку эти данные хранятся в виде единственного поля, то с использованием переменных шаблона невозможно непосредственно осуществить выборку из этого поля данных об имении фамилии. Нос помощью функции explode$, которая принимает единственный строковый параметр и преобразует его в многозначное значение, мы можем трансформировать строку, возвращаемую функцией readline, в многозначное значение, которое должно быть представлено в виде ряда полей в факте user' s-name. Применение функции readline в сочетании с функцией explode/ демонстрируется в следующем диалоге. Игра в палочки 649 очереди 1, 2 или 3 палочки. Весь секрет выигрыша в этой игре (или эвристика) состоит в том, что можно вынудить противника проиграть, если при вашей очереди хода останутся 2, 3 или 4 палочки, те. необходимо к этому стремиться. Таким образом, игрок, приходе которого осталось палочек, проиграл. Чтобы вынудить другого игрока столкнуться с ситуацией, в которой для него остается 5 палочек, следует всегда оставлять после своего хода 5 палочек плюс некоторое количество палочек, кратное 4. Иными словами, следует всегда стремиться к тому, чтобы после вашего хода количество оставшихся в куче палочек было равно 5, 9, 13 и т.д. Если же выходите первыми количество палочек в куче равно одному из этих "безвыигрышных" чисел, то вы не сможете выиграть, при условии безошибочной игры противника. С другой стороны, если ваш ход — первый, а количество палочек в куче не соответствует одному из "безвыигрышных" чисел, то вы всегда можете рассчитывать на победу. Прежде чем программа сможет приступить к ведению игры Sticks, она должна определить некоторую информацию. Прежде всего программа должна играть против противника-человека, поэтому необходимо в первую очередь выяснить, кто ходит первым. Кроме того, следует определить начальный размер кучи. Эта информация может быть помещена в конструкцию беййася. Но весьма несложно также запросить такую информацию у того, кто собирается состязаться с программой, чтобы он ввел необходимые сведения с клавиатуры. Следующий пример показывает, как применяется функция read для ввода данных initial-phase (phase choose-player)) (defrule player-select (phase choose-player) — > (printout t "Who moves first (Computer: с "Human: h)? ") (assert (player-select (read)))) (defrule good-player- choice ?phase <- (phase choose-player) ?choice <- (player-select ? player&c h) — > (retract ?phase ?choice) (assert (player-move ? р1ауег))) В обоих правилах используется шаблон (phase choose — player) для указания на то, что эти правила применимы только при наличии конкретного факта в списке фактов. Такой шаблон называется управляющим шаблоном, поскольку он специально предназначен для управления тем, является ли правило применимым или нет. А для запуска управляющего шаблона используется управляющий Глава 8. Развитые средства сопоставления с шаблонами факт. В управляющем шаблоне для этих правил используются только литеральные поля, поэтому управляющий факт должен точно согласовываться с шаблоном. В рассматриваемом случае управляющим фактом, применяемым для запуска этих правил, должен быть факт (phase choose — player) Кроме всего прочего, такой управляющий факт позволяет исправить ошибку, если входные данные, полученные с помощью функции read, не совпадают с ожидаемыми значениями, "с" или "h" (сокращения от "computer" — компьютер и "human" — человек. Повторную активизацию правила player — select можно обеспечить, извлекая и вновь вводя в список фактов этот управляющий факт. Ниже приведен вывод, который показывает, как работают правила player— select и good — player — choice при определении того, кто должен ходить первым. CLIPS> (unwatch all)J CLIPS> (watch facts)J CLIPS> (reset)J > f — Ос с 1 > f — 2 (player-select с) < — f-1 (phase choose-player) < — f-2 (player-select с) > f — 3 (player — move с) CLIPS> Эти правила функционируют должным образом, если вводится предусмотренный в них ответ с или h, но если введенный ответ отличается от требуемого, то проверка на наличие ошибок не осуществляется. Это — один из примеров многочисленных ситуаций, в которых необходимо повторить запрос на ввод, чтобы исправить ошибку при вводе. В приведенном ниже коде показан удобный способ реализации в программе циклического повтора запроса на ввод. Правило, приведенное в этом коде, при использовании в сочетании с правилами player — select и good-player — choice обеспечивает проверку на наличие ошибок. (defrule bad-player-choice ?phase <- (phase choose-player) ?choice <- (player-select ?player&-c&-h) — > (retract ?phase ?choice) (assert (phase choose-player)) (printout t "Choose с or h." crlf)) 8.8. Предикативные функции 651 Ив этом случае следует отметить, что используется управляющий шаблон (phase choose — player) Он обеспечивает общее управление над циклом ввода, а также предотвращает запуск этой группы правил при осуществлении других этапов выполнения программы. Здесь заслуживает внимания то, как действуют совместно два правила, player — select и bad — player — choice, а именно, как каждое правило предоставляет факты, необходимые для активизации другого правила. Если ответ на вопрос о выборе игрока, начинающего игру, является недопустимым, то с помощью правила bad — player — choice извлекается управляющий факт (phase choose — player), после чего этот факт снова вводится, что вызывает повторную активизацию правила player — select. 8.8 Предикативные функции По определению как предикативная функция может рассматриваться любая функция, возвращающая либо символ, либо символ FALSE. По существу при выполнении любых операций в рамках предикативной логики в языке CLIPS любое значение, отличное от символа FALSE, рассматривается как символ TRUE. Можно также считать, что предикативной функцией является функция, имеющая булево возвращаемое значение. Предикативные функции подразделяются на две категории заранее определенные функции и определяемые пользователем функции. Заранее определенными функциями называются функции, которые уже предусмотрены в языке. С другой стороны, определяемыми пользователем или внешними функциями, являются функции, отличные от заранее определенных, которые написаны на языке С или на другом языке, а код реализации этих функций связан с интерпретатором CLIPS. В приложении Д содержится список заранее определенных предикативных функций предназначенных для выполнения булевых логических операций, операций сравнения значений и операций проверки на наличие конкретного типа. Примеры применения некоторых из этих функций показаны в следующем диалоге CLIPS> (and (> 4 3) (> 4 5))J FALSE CLIPS> (or (> 4 3) (> 4 5) ) 1 TRUE CLIPS> (> 3) J TRUE CLIPS> (( б 2) 1 FALSE CLIPS> (integerp 3)J Глава 8. Развитые средства сопоставления с шаблонами 652 CLIPS> (integerp 3.5) FALSE CLIPS> 8.9 условный элемент test (test ) В качестве примера предположим, что очередность хода принадлежит игроку- человеку. Если в куче осталась только одна палочка, то человек проиграл. А если палочек больше одной, то компьютер должен спросить человека, сколько палочек следует убрать из кучи. В том правиле, в котором формулируется вопрос к человеку о том, сколько палочек следует убрать из кучи, необходимо предусмотреть проверку, что количество палочек в куче больше одной. Как показано в следующем условном элементе test, для представления этого ограничения может использоваться предикативная функция >; в рассматриваемом выражении переменная size обозначает количество палочек, оставшихся в куче (test (> ?size 1)) После того как человек укажет количество палочек, подлежащих удалению, его ответ необходимо проверить, чтобы убедиться в том, что он является допустимым. Количество удаляемых палочек должно быть выражено целым числом, Часто встречаются такие ситуации, в которых было бы целесообразно повторить вычисления или еще раз выполнить какую-либо операцию обработки информации. Общепринятым способом решения такой задачи является создание цикла. В предыдущем примере цикл создавался для повторного вывода вопроса, который появлялся на экране до тех пор, пока от пользователя не поступал правильный ответ. Ново многих других ситуациях цикл должен завершаться автоматически, безучастия человека, в результате вычисления какого-то произвольного выражения. В основе одного из мощных способов вычисления выражений в левой части правила может лежать условный элемент test. В частности, условный элемент test не требует сопоставления шаблона с одним из фактов в списке фактов, а просто вычисляет выражение. Но самой внешней функцией этого выражения должна быть предикативная функция. Если вычисление выражения приводит к получению любого значения, отличного от символа FALSE, то проверка с помощью условного элемента test завершается успешно. Если же вычисление выражения приводит к получению символа, то проверка с помощью условного элемента test оканчивается неудачей. Запуск правила происходит только в том случае, если проверка всех его условных элементов test, наряду со всеми другими шаблонами, завершается успешно. Условный элемент test имеет следующий синтаксис. Условный элемента также должно быть больше или равно одному и меньше или равно трем. Кроме того, человек не может взять больше палочек, чем имеется в куче, и его необходимо вынудить взять последнюю палочку. Как показано в следующем условном элементе test, в котором переменная будет содержать количество взятых из кучи палочек, а переменная ? size представляет собой количество палочек, оставшихся в куче, предикативная функция and может использоваться для выражения всех этих ограничений (test (and (integerp ?choice) (>= ?choice 1) (<= ?choice 3) (< ?choice Аналогичным образом, как неправильно заданное количество палочек, которые должны быть взяты из кучи, рассматривается значение, не являющееся целочисленным, меньшее единицы или большее трех, а также большее или равное количеству оставшихся палочек (если количество палочек больше одной). Эти условия можно представить с помощью предикативной функции or, как показано в следующем условном элементе в котором переменная ? choice будет содержать данные о количестве палочек, которые должны быть взяты из кучи, а переменная ? size представляет количество палочек, оставшихся в куче (test (or (not (integerp ?choice)) (< ?choice 1) (> ?choice 3) (>= ?choice ?size))) Ниже приведены правила, в которых описанные здесь условные элементы test применяются для проверки того, являются ли допустимыми данные о количестве палочек, взятых из кучи игроком-человеком. В этих правилах для хранения информации о количестве палочек, оставшихся в куче, используется факт pile-size. (defrule get- human-move (player-move h) (pile-size ?size) У человека-игрока есть выбор, только если в куче осталось больше одной палочки (> ?size 1)) — > (printout t "How many sticks do you wish to take?") (assert (human-takes (read)))) (defrule Глава 8. Развитые средства сопоставления с шаблонами 654 ? whose-turn <- (player-move h) (pile-size ?size) ?number-taken <- (human-takes ?choice) (test (and (integerp ?choice) (>= ?choice 1) (<= ?choice 3) (< ?choice ?size))) (retract ?whose-turn ?number- taken) (printout t "Human табе а valid move" crlf)) (defrule bad- human-move ?whose-turn <- (player-move h) (pile-size ?size) ? number-taken <- (human-takes ?choice) (test (or (not (integerp ? choice)) (< ?choice 1) (> ?choice 3) (>= ?choice ?size))) (printout t "Human табе an invalid move" crlf) (retract ?whose-turn ?number- taken) (assert (player-move h))) 8.10 Предикативное ограничение поля Для выполнения предикативных проверок непосредственно в шаблонах могут использоваться так называемые предикативные ограничения поля, выраженные с помощью символа. Применение такого ограничения во многих отношениях аналогично выполнению проверки с помощью условного элемента test непосредственно после сопоставления с шаблоном, нов некоторых случаях, как будет показано в главе, предикативные ограничения поля являются более эффективными по сравнению с условными элементами Предикативное ограничение Выполнение данной программы завершается только после того, как будет введено допустимое значение количества взятых из кучи палочек. Ив этом случае для повторной активизации правила, позволяющего еще раз ввести ответ после ввода в предыдущей попытке недопустимого ответа, применяется управляющий факт. Повторный ввод управляющего факта (player — move h) для еще одной активизации правила bad — human — move осуществляется с помощью правила get — human — move. После этого игрок- человек получает возможность еще раз указать количество палочек, которые должны быть взяты из кучи. Предикативное ограничение поля 655 поля может использоваться полностью аналогично литеральному ограничению поля. Кроме того, предикативное ограничение поля может быть задано в поле отдельно или введено как одна из частей более сложного поля с помощью соединительных ограничений поля -, & и . За предикативным ограничением поля всегда следует функция, подлежащая вызову на выполнение. Как ив случае условного элемента test, эта функция должна быть предикативной. В качестве примера рассмотрим следующие два шаблона, применявшиеся в правиле get — human — move (которое описано в предыдущем разделе, для проверки того, что в куче содержится больше одной палочки (pile-size ?size) (test (> ?size 1)) Эти два шаблона можно заменить таким одним шаблоном (pile-size ?size&:(> ? size 1)) Предикативное ограничение поля может использоваться отдельно, но ситуации, в которых такая возможность является действительно удобной, встречаются редко. Как правило, осуществляется связывание переменной, а затем проверка с помощью предикативного ограничения поля. При чтении шаблона предикативное ограничение поля можно рассматривать как означающее "такой, что. Например, ограничение поля, приведенное выше ?яхе&:(> ?size 1) можно прочитать как "выполнить привязку значения ? size, такого, что ? size больше 1". Одной из областей применения предикативного ограничения поля является проверка наличия ошибок в данных. Например, в следующем правиле выполняется проверка для определения того, что элемент данных является числовым, перед его сложением с промежуточной суммой (defrule add-sum (data-item ?value&:(numberp ?value)) ?old-total <- (total ?total) — > (retract ?old-total) (assert (total (+ ?total ?value)))) Второе поле первого шаблона можно прочитать как "выполнить связывание значения ?value, такого, что ?value является числом. Ниже приведены два правила, в которых выполняется такого же рода проверка наличия ошибок и демонстрируется использование соединительных ограничений поля - ив сочетании с предикативным ограничением поля. (defrule find-data-type-1 (data ?item&:(stringp ?item):(symbolp Глава 8. Развитые средства сопоставления с шаблонами 656 (printout t ?item " is а string or symbol crlf)) (defrule find-data-type-2 (data ?item&-:(integerp ?item)) => (printout t ?item " is not an integer " crlf)) В правиле find — data — type-1 проверяется, является ли элемент данных строкой или символом для этого используются такие предикативные функции, как stringp и symbolp. Правило find — data — type-2 обеспечивает проверку того, что элемент данных не является числовым для этого вызывается на выполнение функция integerp, а затем применяется соединительное ограничение - для получения логического значения, являющегося отрицанием значения, возвращаемого функцией integerp. 8.11 Ограничение поля для возвращаемого значения (deftemplate take-sticks (slot how-many) (slot for- remainder)) (deffacts take-sticks-information (take-sticks (how-many 1) (for-remainder 1)) (take-sticks (how-many 1) (for-remainder 2)) (take-sticks (how-many 2) (for-remainder 3)) (take-sticks (how-many 3) (for-remainder 0))) (defrule computer-move Ограничение поля для возвращаемого значения, обозначаемое символом позволяет использовать для сравнения внутри шаблона возвращаемое значение функции. Ограничение поля для возвращаемого значения может применяться в сочетании с соединительными ограничениями поля -, & и , а также с предикативным ограничением поля. Как и за предикативным ограничением поля, за ограничением поля для возвращаемого значения должен следовать вызов функции. Но вызываемая функция необязательно должна быть предикативной функцией. Единственным ограничением является то, что вызываемая функция должна иметь однозначное возвращаемое значение. Ниже приведено правило, которое показывает, как можно использовать ограничение возвращаемого значения в программе Sticks для определения количества палочек, которые игрок-компьютер должен убрать из кучи. Ограничение поля для возвращаемого значения 657 ? whose-turn <- (player-move с) ?pile <- (pile-size ?size) (test (> ?size 1)) (take-sticks (how-many ?number) (for-remainder =(mod ?size 4))) => (retract ?whose-turn ?pile) (assert (pile-size (- ?size ?number))) (assert (player-move h))) Подходящее количество палочек, которое компьютер должен взять на своем ходе, определяется с помощью правила computer — move. Первый условный элемент шаблона гарантирует, что правило применяется, только если ходить должен компьютер. Второй и третий условные элементы выполняют проверку для определения того, измеряется ли количество оставшихся палочек числом больше единицы. Если оказалось, что количество оставшихся палочек составляет только одну палочку, то компьютер будет вынужден взять ее и проиграть. Последний шаблон действует в сочетании с конструкцией def facts с именем take — sticks — information для определения подходящего количества палочек, которые должны быть взяты из кучи. Функция mod (сокращение от modulus) возвращает целочисленный остаток отделения своего первого параметра на второй. При чтении шаблона целесообразно рассматривать ограничение поля для возвращаемого значения как имеющее смысл "равно. Например, ограничение поля = (mod ?size 4) можно прочитать как "это поле равно остатку отделения значения переменной ?size на 4". Компьютер должен попытаться взять такое количество палочек, чтобы этот остаток отделения общего количества палочек на четыре стал равным единице. А если указанный остаток равен единице сразу после того как очередность хода переходит к компьютеру, то компьютер неизбежно проиграет, при условии, что его противник не допустит ошибку. В данном случае компьютер берет только одну палочку, чтобы затянуть игру (в надежде, что игрок-человек допустит ошибку. Во всех других случаях компьютер может взять такое подходящее количество палочек, которое вынудит проиграть его противника. Для того чтобы ознакомиться более подробно стем, как действует ограничение поля для возвращаемого значения, рассмотрим конкретный пример. Предположим, что в куче находятся 7 палочек и очередность хода принадлежит компьютеру. Проверка первых трех условных элементов правила computer — move завершится успешно, и останется только проверить последний условный элемент. В |