дб. Четвертое издание джозеф Джарратано Университет Хьюстон клиэрЛэйк Гари Райли People5oft, Издательский дом "Вильямс" Москва СанктПетербург Киев 2007 ббк 32. 973. 26 018 75 Д
Скачать 3.73 Mb.
|
answer)) Данное объявление de f function можно непосредственно преобразовать в объявление de заменив имя конструкции, таким образом (defmethod check-input (?question $?values) (printout t ?question " " ?values " ") (bind ? answer (read)) (while (not (member$ ?answer ?values)) (printout t ? question " " ?values " ") Если не считать того, что в этой форме вместо терма заданная в качестве параметра Тело метода, как и правая часть конструкции de f rule или тела конструкции def function, — это ряд выражений, которые выполняются в указанном порядке после вызова метода Глава 10. Процедурное программирование (bind ?answer (read))) (return ?answer)) (defrule get-human-move (player — move h) (pile-size ?size) (test (> ?size 1)) — > (printout t "How many sticks do you wish to take? ") (assert (human-takes (read)))) (defrule good- human-move ?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 made а 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) Следует отметить, что объявление конструкции de f function нельзя заменить объявлением универсальной функции стем же именем, или наоборот, поэтому, чтобы иметь возможность определить конструкцию de fmethod с именем check — необходимо вначале удалить конструкцию de f f unction с именем check — input. Конструкция de fmethod с именем check- input, объявленная выше, действует полностью идентично ранее рассматривавшейся в данной главе конструкции de f Тем не менее, если не будут определены еще какие-либо методы стем же именем, тов действительности нет смысла использовать универсальную функцию, а не применявшуюся ранее конструкцию беййыпсоп. Поэтому рассмотрим еще одну часть программы Sticks. Как было отмечено в разделе 8.9, для определения количества палочек, удаленных игроком- человеком, используются следующие три правила. Конструкции defgeneric и defmethod 815 (>= ?choice ?size))) — > (printout t "Human made an invalid move" crlf) (retract ?whose- turn ?number-taken) (assert (player-move h))) Безусловно, количество палочек, которое может быть разрешено удалить человеку, не всегда равно 1, 2 или 3, поэтому невозможно просто вывести для пользователя приглашение с указанием количества палочек, как в следующем вызове (check-input " How many sticks do you wish to take?" 1 2 3) Вместо этого количество удаляемых палочек должно вычисляться динамически, как показывает следующее модифицированное правило get — human — move: (defrule get-human-move (player — move h) (pile- size ?size) (test (> ?size 1)) => (bind ?responses (create$)) (bind ? upper-choice (min (- ?size 1) 3)) (loop-for-count (?i ?upper-choice) (bind ?responses (create$ ?responses ?i))) (bind ?answer (check- input "How many sticks do you wish to take?" ?responses)) (assert (human-takes ?answer))) В первых четырех строках правила get — human — move динамически создается многозначное значение, содержащее множество допустимых значений, из которых может выбирать пользователь. Это множество может состоять только из чисел 1, 2 или 3, но должно также быть меньше количества оставшихся палочек. Нов программе было бы неудобно вычислять все допустимые ответы, а поскольку всевозможные ответы отображаются при выводе вопроса пользователю, то если бы в правиле check — input пришлось передать просьбу пользователю выбрать одно значение от 1 до, то было бы необходимо вывести все 100 значений. Эту проблему можно устранить, введя дополнительный метод. В частности, ниже приведен метод, в котором используются ограничения типа для осуществления особого принципа действия, в котором после вопроса в вызове правила check — input задаются два целых числа. (defmethod check-input ((? question STRING) 816 Глава 10. Процедурное программирование (?valuel INTEGER) (?value2 INTEGER)) (printout t ?question " (" ?value1 "-" ?value2 ") ") (bind ?answer (read)) (while (or (not (integerp ? answer)) (( ?answer ?value1) (> ?answer ?value2)) (printout t ? question " (" ?valuel "-" ?value2 ") ") (bind ?answer (read))) (return ? answer)) В этом методе частично используется такой же код, как ив первоначальном методе as k — user, но имеются существенные различия, из которых наиболее важным является список параметров. Каждый из параметров заключается в круглые скобки, а также содержит спецификацию типа. Спецификацией типа для переменной ?question является, для переменной ?value1 — INTEGER, а для переменной ?value2 — также INTEGER. В связи с наличием таких ограничений данный конкретный метод вызывается только если функции ask — user передаются три параметра, причем первый из них имеет тип STRING, второй INTEGER и третий — также INTEGER. Еще одной отличительной особенностью данного метода является то, что вслед за вопросом в нем предусматривается вывод информации о диапазоне допустимых целых чисел, а не выводится каждое допустимое целое число. Наконец, еще одной особенностью данного метода является то, что проверка значения members в цикле while заменена в нем тремя проверками, позволяющими убедиться в том, что в качестве ответа введено целое число и что это число находится в допустимом диапазоне. После определения обоих этих методов экспериментальный прогон, результаты которого приведены ниже, показал, что оба метода работают должным образом. CLIPS> (check-input "Pick а number" 1 3)J Pick а number (1-3) а Pick а number (1-3) 34J Pick а number (1-3) 3 3 CLIPS> (check-input "Pick а number" 1 2 3) Pick а number (1 2 3) а Pick а number (1 2 3) 34 Pick а number (1 2 3) 3 3 CLIPS> 10.5. Конструкции de f generic и de fmethod 817 Обратите внимание на то, что после выбора первого метода был отображен допустимый диапазона после вызова второго метода на внешнее устройство выведен список допустимых значений, (1 2 3) . Приоритеты методов После вызова универсальной функции в системе CLIPS выполняется только один из ее методов. Процесс определения того, какой метод должен быть выполнен, называется вызовом перегруженного метода. В предыдущем примере было показано, что в универсальной функции check — input применяется другой принцип действия при ее вызове с указанием целочисленного диапазона, а не списка допустимых значений. Вторым выражением в рассматриваемом примере было следующее "Pick а number" 1 2 3) Две сигнатуры методов функции check — input были определены таким образом (? question $?values) (?question STRING) (?value1 INTEGER) (? value2 INTEGER) Очевидно, что сигнатура первого метода может использоваться для вычисления этого выражения, поскольку она требует один или больше параметров, но сигнатура второго метода неприменима для вычисления этого выражения, так как она требует точно три параметра, а задано четыре. С другой стороны, анализ первого выражения в рассматриваемого примере (check-input "Pick аи его сравнение с сигнатурами объявленных методов показывает, что данное выражение может быть обработано с помощью обеих сигнатур методов. Еще раз отметим, что сигнатура первого метода просто принимает от одного или больше параметров любого типа, а сигнатура второго метода предусматривает применение точно трех параметров, причем первый параметр должен иметь тип, второй — INTEGER и третий — INTEGER. Поскольку выражение содержит параметры точно в таком количестве и такого типа, как требуется, данный метод также является применимым. Каким же образом система CLIPS определяет метод, подлежащий вызову на выполнение, если применимыми являются несколько методов Ответ на этот вопрос состоит в том, что система CLIPS определяет порядок приоритетов методов, относящихся к данной конкретной универсальной функции. Если применимыми являются несколько методов, тона выполнение вызывается метод с самым высоким приоритетом. Для ознакомления с порядком приоритетов методов, применимых для обработки конкретного множества параметров, может 818 Глава 10. Процедурное программирование использоваться команда preview-generic. Эта команда имеет следующий синтаксис) Параметр — name> представляет собой имя универсальной функции, а параметр Определяются лишь все применимые методы, а затем отображаются в виде списка в порядке приоритетов, например, как показано ниже. CLIPS> (preview-generic check-input "Pick а number" 1 3)J check-input «2 (STRING) (INTEGER) (INTEGER) check-input «1 () S? CLIPS> Этот вывод подтверждает то, что было уже определено опытным путем метод check — input, в котором проверяется принадлежность к целочисленному диапазону, имеет более высокий приоритет, чем метод check — input, в котором проверяется значение с использованием параметра с подстановочным символом. В первую очередь в списке отображается метод «2, те. метод проверки принадлежности к целочисленному диапазону, а это означает, что данный метод имеет более высокий приоритет. В этом списке вначале отображается имя универсальной функции, а затем индекс метода. После этого выводятся допустимые типы для каждого параметра в круглых скобках. Имена переменных в списке параметров конструкции de fmethod не влияют на приоритет, поэтому включать их в список нет необходимости. Следующим по порядку в списке находится метод «1. На первый параметр не налагаются какие-либо ограничения типа, поэтому отображается пустая пара круглых скобок, ( ) . Последним параметром в методе «1 является параметр с подстановочным символом. Если для параметра с подстановочным символом не заданы какие-либо ограничения типа, то команда preview-generic отображает для представления этого параметра символ Если в системе CLIPS приходится проводить различия между двумя методами универсальной функции, то для определения метода с более высоким приоритетом используются описанные ниже этапы. 1. Сравнить самые левые неисследованные ограничения параметров этих двух методов. Если очередной параметр в списке параметров имеется только водном методе, перейти к шагу 6. Если неисследованные параметры не остались нив одном методе, перейти к шагу 7. В противном случае перейти к шагу 2. 10.5. Конструкции defgeneric и defmethod 819 2. Если один параметр является обычным параметром, а другой параметром с подстановочным символом, то метод с обычным параметром имеет более высокий приоритет. Если оба параметра не различаются поданному признаку, перейти к шагу. 3. Если водном из параметров заданы ограничения типа, а в другом параметре — нетто метод с ограничениями типа имеет более высокий приоритет. Если оба параметра не различаются поданному признаку, перейти к шагу 4. 4. Сравнить крайние левые неисследованные ограничения типа двух параметров. Если ограничение типа водном параметре является более конкретным по сравнению с ограничением типа во втором параметре, то метод с более конкретным ограничением типа имеет более высокий приоритет. Например, ограничение типа является более конкретным, чем NUMBER. Если ни одно ограничение типа не является более конкретным (например, ограничение типа INTEGER не более конкретно, чем) ив обоих параметрах есть еще неисследованные ограничения типа, то повторить шаг 4. Если же водном из параметров есть еще неисследованные ограничения типа, а в другом параметре нетто метод без дополнительных ограничений типа имеет более высокий приоритет. Если оба параметра не различаются поданному признаку, перейти к шагу. 5. Если водном параметре имеется ограничение запроса, а в другом — нетто метод с ограничением запроса имеет более высокий приоритет. В противном случае возвратиться к шагу 1 и сравнить следующее множество параметров. 6. Если следующим параметром метода, в котором остались неисследованные параметры, является обычный параметр, то данный метод имеет более высокий приоритет. В противном случае более высокий приоритет имеет другой метод. 7. Более высокий приоритет имеет метод, который первым прошел проверку. Блок-схема процедуры, позволяющей получить результат, эквивалентный тому, который может быть получен с помощью шагов от 1 до 7, показан на рис. 10.1. Шаг начинается с действия, обозначенного прямоугольником в левом верхнем углу рисунка. А на рис. 10.2 показана блок-схема процедуры, позволяющей получить результат, эквивалентный тому, который может быть получен с помощью шагов 3 и касающийся определения того, имеет ли один параметр более конкретные ограничения типа по сравнению с другим параметром. Используя описанные выше шаги, можно легко определить, почему метод «2 универсальной функции check — input имеет более высокий приоритет, чем метод «1. Начиная с шага 1, вначале сравним крайние левые неисследованные параметры обоих методов. Ограничением параметра для метода является ( ), а ограничением параметра для метода «2 — (STRING) Поскольку оба метода 820 Нет Да Нет Нет-Э» Да Да — » Нет Да Нет Да -Э Нет Рис. 10.1. Определение приоритета метода Сравнить крайние левые неисследованные параметры каждого метода Глава Процедурное программирование 10.5. Конструкции def generic и defmethod 821 Да Нет Сравнить крайний левый неисследованный тип каждого метода Только один параметр имеет тип, подлежащий сравнению Да Нет Нет-Э» Да Да — Э Нет Рис. 10.2. Определение того, являются ли ограничения типа одного параметра более конкретными по сравнению с другими параметрами имеют и другие параметры, переходим к шагу 2. Оба этих параметра являются обычными параметрами, поэтому переходим к шагу На этом шаге выясняем Глава 10. Процедурное программирование что ограничение типа имеет только метод №2, и это означает, что метод имеет более высокий приоритет по сравнению с методом Итак, метод №2 универсальной функции check — input позволяет вводить только целочисленные значения, поэтому создадим еще один метод, который даст возможность вводить значение с плавающей запятой, принадлежащее к указанному диапазону, следующим образом (defmethod check-input ((? question STRING) (?value1 NUMBER) (?value2 NUMBER)) (printout t ?question " (" (float ?value1) — (float ?value2) ") ") (bind ? answer (read)) (while (or (not (numberp ?answer)) (< ?answer ? value1) (> ?answer ?value2)) (printout t ?question " (" (float ?value1) — (float ?value2) II ) 11 ) (bind ?answer (read))) (return Этот метод является аналогичным описанному ранее методу проверки принадлежности к целочисленному диапазону, за исключением того, что в ограничениях параметров вместо типа задан тип NUMBER, при отображении допустимого диапазона осуществляется принудительное преобразование целочисленных значений диапазона в значения с плавающей точкой, а для проверки на наличие недопустимых типов используется предикативная функция numberp, а не После определения этого дополнительного метода был проведен экспериментальный прогон, который показал, что оба метода проверки принадлежности к диапазону работают должным образом, как показано ниже. CLIPS> (check-input "Pick а number" 1 3.5) 1 Pick а number (1.0-3.5) б 1 Pick а number (1.0- 3.5) 2.7 1 2.7 CLIPS> (при. "Pick а number" 1 3)J Pick а number (1-3) 2.7 ! Pick а number (1-3) 2 2 CLIPS> 10.5. Конструкции de f generic и de fmethod 823 С помощью команды preview — generic можно показать, что для обработки второго вызова универсальной функции check — input из приведенных выше вызовов применимы все три метода CLIPS> (preview-generic check-input "Pick а number" 1 3)J check-input №2 (STRING) (INTEGER) (INTEGER) check-input №3 (STRING) (NUMBER) (NUMBER) check-input №1 () ? CLIPS> Метод с двумя ограничениями INTEGER имеет более высокий приоритет, чем метод с двумя ограничениями NUMBER. Причину этого можно легко выяснить с помощью той же описанной выше процедуры определения порядка приоритетов. Начиная с шага 1, сравним крайние левые неисследованные параметры обоих методов. Ограничением параметра для методов «2 и является ( STRING) . Оба метода имеют и другие параметры, поэтому перейдем к шагу 2. Поскольку оба параметра являются обычными параметрами, перейдем к шагу 3. Оба параметра имеют ограничения типа, поэтому перейдем к шагу 4. В связи стем, что оба ограничения типа являются одинаковыми, перейдем к шагу 5. Ни один из параметров не имеет ограничения запроса, следовательно, мы должны возвратиться к шагу 1. Крайним левым неисследованным ограничением параметра для метода «2 является (INTEGER), а для метода крайним левым неисследованным ограничением параметра является (NUMBER) . Оба метода имеют параметры, поэтому перейдем к шагу 2. Поскольку оба параметра представляют собой обычные параметры, перейдем к шагу 3. Оба параметра имеют ограничения типа, поэтому перейдем к шагу 4. А в связи стем, что ограничение типа INTEGER в методе «2 является более конкретным, чем ограничение типа NUMBER в методе метод №2 имеет более высокий приоритет. Рассмотрим еще один пример определения приоритета. В данном случае определим метод, который по существу является таким же, как и последний определенный метод, за исключением того, что вместо использования NUMBER в качестве ограничения типа определим другие два типа, INTEGER и FLOAT: (defmethod check-input ((?question STRING) (?value1 INTEGER FLOAT) (? value2 INTEGER FLOAT)) (printout t ?question " (" (float ?value1) — |