дб. Четвертое издание джозеф Джарратано Университет Хьюстон клиэрЛэйк Гари Райли People5oft, Издательский дом "Вильямс" Москва СанктПетербург Киев 2007 ббк 32. 973. 26 018 75 Д
Скачать 3.73 Mb.
|
более удобном для чтения, как показано ниже. (deffunction hypotenuse-length (а ?b) (bind ?temp (+ (* а а) (* ?b ?b))) (** ?[ (return (** ?temp 0.5))) или (deffunction hypotenuse-length (а Двумя параметрами этой функции являются аи эти параметры используются для передачи в функцию значений длины двух сторон треугольника, примыкающих к прямому углу. Функция ** со вторым параметром 0.5 применяется для вычисления квадратного корня, поскольку значение выражения заданную вторым параметром. После того как функция hypotenuse — length будет определена, ее можно вызывать из приглашения к вводу команд, следующим образом CLIPS> (hypotenuse-length 3 4) 1 5 ° 0 CLIPS> 10.3. Конструкция de f f unction 791 (bind ?temp (+ (* а асс) Но возвращаемым значением указанной конструкции def function является результат вычисления последнего выражения, а вычисление всех выражений в конструкции de f function с именем hypotenuse — length осуществляется последовательно, поэтому нет необходимости применять явно заданный оператор Необходимость в использовании функции return возникает главным образом, если удовлетворяется условие, согласно которому должно быть завершено выполнение конструкции def которое подлежит вычислению. Например, ниже приведена конструкция de f function, которая определяет, является ли заданное число простым. (deffunction primep (?num) (loop-for- count (?i 2 (- ?num 1)) (if (= ?num (* (div ?num ?i) ?i)) then (return FALSE))) (return TRUE)) Конструкция de f function с именем primep возвращает символ TRUE, если параметр ? num простое число в противном случае она возвращает символ. Число является простым, только если его делителем служит число 1 или оно само. Таким образом, если число делится без остатка на любое другое число, тоне может быть простым. В конструкции def function с именем primep используется функция loop- for — count для итерации по всем числам от 2 до того числа, которое на единицу меньше числа, проверяемого в качестве "кандидата название" простого числа num (данная программа немного упрощена, так как было бы достаточно проверить возможные делители, не превышающие корня квадратного из числа ? num). Если окажется, что какое- либо из этих чисел делит без остатка число ? num, то функция завершает свою работу и возвращает символ FALSE, поскольку рассматриваемое число не является простым. Для определения статуса числа ? num как простого используется функция Функция div выполняет целочисленное деление и дает в результате целочисленное возвращаемое значение. Поэтому выражение ( div 5 2 ) возвращает 2, а не значение 2 . 5, которое было бы возвращено при использовании выражения (/ 5 2 ) Если выражение (* (div ?num ? i) ? i) возвращает первоначальное значение?пыт, то число ?num делится без остатка на ?i и поэтому число ?num не является простым. А в том случае, если ни одно из значений ?i, применяемых в качестве делителя в функции loop- for-count, не делит число? пыл без остатка, то 792 Глава 10. Процедурное программирование Еще один вариант программы Sticks Как было указано в разделе 8.7, для определения того, должен ли первым ходить компьютер или человек, применяются конструкция de f f acts и три правила 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ауег))) (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)) Очевидно, что необходимость проверять введенные данные встречается часто, поэтому рассмотрим следующую функцию, которая позволила бы в данном случае исключить необходимость проводить такую проверку во всех трех правилах (deffunction check-input (?question ?values) (printout t ? question " " ?values " ") (bind ?answer (read)) (while (not (members ?answer ?values)) (printout t ?question " " ?values " ") (bind ?answer (read))) (return ?answer)) Строка 1 Строка 2 Строка 3 Строка Строка 5 Строка б Строка 7 ?num — простое число, и значение, возвращаемое конструкцией de f function, становится равным. Конструкция с1еййыпсоп 793 Строка 1 начинается с определения конструкции def function с именем Определяемая здесь функция принимает два параметра ? question — приглашение с вопросом, отображаемое для пользователя, и values— список значений, которые допускается вводить в ответ на вопрос. В строке 2 выводятся приглашение с вопросом, а также список допустимых значений. Ответ пользователя на вопрос перехватывается в строке 3 с помощью функции read. В строке 4 начинается цикл while, итерации которого выполняются до тех пор, пока пользователь не введет допустимый ответ. Функция member$ возвращает символ TRUE, если ее первым параметром является один из элементов многозначного значения, представленного в виде второго параметра. В данном случае, если ответ, предоставленный пользователем, ?answer, не является элементом списка допустимых значений, ? values, то выполняется тело цикла Строки 5 и 6, формирующие тело цикла while, обеспечивают передачу пользователю повторяющихся вопросов. После того как пользователь предоставит допустимый ответ, цикл while завершается и выполняется строка 7, которая возвращает допустимый ответ, введенный пользователем. 1 Пример использования конструкции de f f unction с именем check — input приведен ниже. Для создания многозначного значения из однозначных параметров, которое может быть передано в качестве второго параметра в указанную конструкцию de f f unction, применяется функция сгеа1е$. CLIPS> (check- nput "Who moves first, Computer or Human?" (create$ с h))J Who moves first, Computer or Human? (с h) xJ Who moves first, Computer or Human? (с h) computerJ Who moves first, Computer or Human? (с h) с 1 с CLIPS> С помощью функции check — input правило player — select можно перезаписать следующим образом и тем самым исключить необходимость использования правили ?р1ауег (check-input "Who moves first, Computer or Human?" (create$ с 11))) (assert (player-move ?player))) 794 Рекурсия Конструкции de f f unction могут вызывать в своем теле другие конструкции def function, включая самих себя. В качестве примера укажем, что значение факториала от положительного целого числа и определяется следующим образом и * factorial(n — 1) если и ) 1 factorial(n) = 1 если и = О Таким образом, чтобы вычислить факториал от и, необходимо вычислить факториал от и — 1. Поэтому факториал числа обозначаемый как 3!) равен 3 * 2!, что, в свою очередь, равно и составляет 3*2*1 или 6. Ниже приведена конструкция de f f unction, позволяющая вычислить факториал любого целого числа. (deffunction factorial (?n) (if (>= ?n 1) then (* ?n (factorial (- ? n 1))) else 1)) Как указано выше, значение (factorial 3) можно вычислить с помощью выражения 3*(factorial 2), затем 3*2* (factorial 1) и, наконец, 3*2*1, что составляет 6. В том, что эти вычисления действительно проводятся правильно, можно убедиться, непосредственно вызывая конструкцию de f f unction с именем factorial, как показано ниже. Предварительные объявления Иногда в конструкциях def function применяются циклические ссылки друг на друга. Например, в конструкции def function А может быть предусмотрен вызов конструкции def function В, которая вызывает конструкцию def function С, а последняя содержит вызов конструкции def function А, как в следующем примере (deffunction А (?n) (if (<= ?n 0) then 1 CLIPS> (factorial 3)J б CLIPS> (factorial 2) ) 2 CLIPS> (factorial 1) 1 1 CLIPS> Глава 10. Процедурное программирование. Конструкция de f function 795 Ниже приведены значения, вычисляемые этими функциями, в более удобном для чтения виде. Все упоминаемые конструкции deffunction должны быть представлены в программе к тому времени, как в процессе синтаксического анализа встречается ссылка на эти конструкции, поэтому возникает проблема. Дело в том, что невозможно откомпилировать ни одну из этих функций, поскольку каждая из них зависит от другой, как показано ниже (deffunction A (?n) (if (< ?n 0) then 1 else (+ 2 (В (- ?n 1)))))J [ЕХРКИРЯВЗ] Missing function declaration for В. ERROR: (deffunction А (?n) (if (<= ?n 0) then 1 else (+ 2 (В CLIPS> else (+ 2 ВВС С (?n) (if (<= ?n 0) then 1 else (- 2 (А (- ?n 1))))) 1 А(п) = 2+ В(п — 1) 1 В(п) = 2 w С(п — 1) 1 С(п) = 2 — А(п — 1) если ив ином случае если ив ином случае если ив ином случае 796 Глава 10. Процедурное программирование CLIPS> (deffunction В (?n))J CLIPS> (deffunction А (?n) (if (<= 7n 0) then 1 else (+ 2 (В (- п 1))))) 3 CLIPS> (deffunction СВ (С (- ?n 1))))) 1 CLIPS> (deffunction С (а) (if (< ?n 0) then 1 else (- 2 (А (- 7n 1))))) 1 CLIPS> В данном примере предварительные объявления были введены непосредственно перед конструкцией def требующей объявления. Обычно, если применяемые в программе правила загружаются из текстового файла, все предварительные объявления сосредоточиваются вначале файла, чтобы проще было изучать и сопровождать код, но единственным обязательным требованием является то, чтобы такие объявления были сделаны до того, как будет определена конструкция de f function, в которой они требуются. Отслеживание работы конструкций def function Если для отслеживания работы конструкций de f function используется команда watch, то каждый раз, когда начинается или заканчивается выполнение конструкции de f f unction, выводится информационное сообщение, например, как показано ниже. Один из способов устранения указанной проблемы состоит в том, чтобы подготовить предварительные объявления для некоторых из функций. В предварительном объявлении дается описание, состоящее из имении списка параметров функции, но тело функции остается пустым. Функция объявлена, и это означает, что на нее можно сделать ссылку, но функция не имеет тела, поэтому не может ссылаться на другие функции. Предварительное объявление может быть в дальнейшем заменено такой версией, которая включает тело, например, как показано ниже. Конструкция de f function 797 CLIPS> (watch deffunctions)J CLIPS> (factorial 2) 1 DFN » factorial ED:1 (2) DFN » factorial ED:2 (1) DFN » factorial ED:3 (0) DFN « factorial ED:3 (0) DFN « factorial ED:2 (1) DFN « factorial ED:1 (2) 2 CLIPS> Обозначение приведенное вначале информационного сообщения, указывает что оно относится к конструкции de f function. Символ показывает, что происходит переход в конструкцию deffunction, а символ говорит о том, что осуществляется выход из конструкции def function. Следующий символ представляет собой имя конструкции def function, в которую входит или из которой выходит программа в данном случае упоминается конструкция de f function с именем factorial. Символ ED является сокращением от "Evaluation Depth" (Глубина вложенности за этим символом следует двоеточие и значение текущей глубины вложенности в виде целого числа. Значение глубины вложенности показывает, как вкладываются друг в друга вызовы конструкций de f function. Отсчет этого значения начинается с нуля, а затем увеличивается на единицу после перехода в каждую очередную конструкцию def function. После выхода из очередной конструкции de f function значение глубины вложенности уменьшается на единицу. Наконец, последним фрагментом информации, отображаемым в данной строке, являются фактические параметры, передаваемые в конструкцию de f function. В данном примере конструкция de f function с именем factorial первоначально вызывается с параметром Глубина вложенности для этого вызова равна 1. Конструкция de f f unction с именем factorial должна быть вызвана снова для вычисления факториала числа 1. Этот вызов имеет глубину вложенности 2, а его параметр равен 1. Следующий вызов применяется для вычисления факториала числа О. Данный вызов имеет глубину вложенности 3, и его параметром является О. Нов конструкции de f function с именем factorial не требуется рекурсия для определения факториала числа О, поэтому нет необходимости снова вызывать конструкцию de f function и можно начать выходить из вложенных вызовов конструкции бей. После выхода из каждой очередной конструкции de f f unction глубина вложенности вызова уменьшается надо тех пор, пока не происходит выход из первоначального вызова конструкции de f function и не осуществляется возврат значения Глава 10. Процедурное программирование 798 Обеспечивается также возможность отслеживать только определенные конструкции de f function; для этого необходимо указать имена этих конструкций в конце команды watch def functions, например, как показано ниже. CLIPS> (unwatch deffunctions)J CLIPS> (watch deffunctions C)J CLIPS> АО А ВАС ОС (О) DFN « ED:2 (1) DFN « А ED:1 (2) 4 CLIPS> Обратите внимание на то, что глубина вложенности для вызова конструкций de f f unction все еще вычисляется, даже несмотря на то, что не предусмотрено отслеживание работы конструкций de f f unction. Параметр с подстановочным символом Если все параметры в списке параметров конструкции def function являются однозначными переменными, то имеет место взаимно однозначное соответствие между количеством этих параметров и количеством параметров, которые должны быть переданы в конструкцию de f function вовремя ее вызова. Иными словами, если предусмотрено три формальных параметра, то вовремя вызова конструкции de f f unction должны быть переданы три значения. Если же последним параметром, объявленным в конструкции de f f unction, является многозначная переменная (такой параметр называется параметром с подстановочным символом, то конструкция бей может быть вызвана с большим количеством параметров, чем указано в списке параметров. Если в списке формальных параметров имеется М параметров, а в вызове конструкции def function передаются параметров, то первые М параметров в вызове конструкции def function отображаются на первые М — 1 параметры в списке параметров. А параметры от М до N в вызове конструкции de f f unction отображаются на М-й параметр в списке параметров в качестве многозначного значения. Конструкция de f f unction 799 В качестве примера еще раз рассмотрим конструкцию de f function с именем check — input и преобразуем ее последний параметр, ? values, в параметр с подстановочным символом, как показано ниже. (deffunction check-input (?question $?values) (printout t ?question " " ?values " ") (bind ?answer (read)) (while (not (member$ ?answer ?values)) (printout t ?question " " ?values " ") (bind ?answer (read))) (return ? answer)) После преобразования последнего параметра в параметр с подстановочным символом отпадает необходимость в использовании функции create$ для создания многозначного значения и передачи его в конструкцию def function, как показывает следующий пример CLIPS> tcheck-input "Nho moves first, Computer or Human?" с h)J Who moves first, Computer or Human? (с h) computerJ Who moves first, Computer or Human? (с h) humanJ Who moves first, Computer or Human? (с h) hJ h CLIPS> В данном случае после вызова конструкции check — input строка "Who moves first, Computer or Human?" связывается с параметром ?question, а оставшиеся параметры, си преобразуются в многозначное значение и сохраняются в параметре с подстановочным символом, $? values. Команды для работы с конструкцией def function Команда сокращение от pretty print deffunction — структурированный вывод конструкции de f f unction) используется для отображения текстового представления конструкции de f йыпсion. Команда undeffunction применяется для удаления конструкции def function. Команда list-deffunctions служит для отображения списка конструкций de f function, предусмотренных в языке. Функция get-deffunction-list возвращает многозначное значение, содержащее список объявленных конструкций de f f unction. Эти команды имеют следующий синтаксис [ 800 Глава 10. Процедурное программирование Ниже приведены примеры применения данных функций. CLIPS> (list-deffunctions)J hypotenuse-length primep check-input factorial А В С For а total of 7 deffunctions. CLIPS> (get-deffunction-list)J (hypotenuse-length primep check-input factorial А В С) CLIPS> (undeffunction primep)J CLIPS> (get-deffunction-list)J (hypotenuse-length check-input factorial А В С) CLIPS> (ppdeffunction factorial)J (deffunction MAIN::factorial (?n) (if (>= ?n 1) then (* ?n (factorial (- ?n 1))) else 1)) CLIPS> Если на некоторую конструкцию de f f unction ссылаются другие конструкции de f f unction или конструкции каких-то других типов, то данная конструкция не может быть удалена. В такой ситуации единственным способом удалении конструкции de f function является уничтожение всех других ссылок или выдача команда clear, как показано в следующем примере CLIPS> (undeffunction A)J [PRNTUTIL4] Unable to delete deffunction A. CLIPS> (deffunction С (?n)) ! CLIPS> АСС. Конструкция de f f unction 801 Функции, определяемые пользователем В языке CLIPS предусмотрена возможность не только объявлять конструкции def function, но и применять способ вызова функций, написанных на языке программирования С. Функции, определяемые с использованием этой методологии, называются функциями, определяемыми пользователем. Данный термин был введен еще тогда, когда в языке CLIPS не была доступна конструкция de f function и единственным способом введения новых функций служило написание этих функций на языке С. Поэтому указанный термин применяется только к функциям, написанным на языке С, даже несмотря на то, что конструкции de f function объявляются пользователем и вполне могут рассматриваться как функции, определяемые пользователем. Подробная информация о том, как создать и ввести в язык CLIPS функцию, определяемую пользователем, приведена в документе Advanced Programming Guide, который включен в компакт-диск, прилагаемый к этой книге. Как правило, заниматься написанием функций, определяемых пользователем, а не применять конструкцию def function, целесообразно только по двум причинам. Первой причиной может стать наличие кода, уже написанного на языке С, который желательно объединить с кодом на языке CLIPS. Во многих случаях повторное написание больших объемов кода на другом языке является нежелательным, поэтому проще оставить этот код написанным на языке Си предусмотреть возможность вызывать его из программы CLIPS с помощью определяемой пользователем функции. Второй причиной может стать быстродействие. Выполнение функции С, откомпилированной в собственный код компьютера, происходит гораздо быстрее по сравнению с конструкцией def function, эксплуатируемой в интерпретирующей среде, которая предоставляется системой CLIPS, особенно если речь о больших фрагментах кода. Но, к счастью, в большинстве задач удобства, создаваемые благодаря тому, что есть возможность определять конструкции def function непосредственно в среде CLIPS, компенсируют снижение быстродействия, вызванное эксплуатацией интерпретируемого кода. Для добавления определяемых пользователем функций к системе CLIPS требуется создание новой исполняемой программы CLIPS, а для тех, кто незнаком с использованием компиляторов или с программированием на языке С, такая задача требует настолько значительных усилий, что не каждый на это решится. Тем не менее в документе Advanced Programming Guide приведено много примеров определяемых пользователем функций кроме того, многочисленные примеры определяемых пользователем функций можно найти в исходном коде CLIPS, который также включен в указанный компакт-диск. Все функции и команды, описанные в главах 7 — 12, были введены в среду CLIPS с использованием методологии создания определяемых пользователем функций Глава 10. Процедурное программирование 10.4 Конструкция de f global Язык CLIPS позволяет определять переменные, сохраняющие свои значения вне области определения той или иной конструкции. Эти переменные называются глобальными переменными. До сих пор все переменные, применявшиеся в конструкциях, представляли собой локальные переменные. Эти переменные являются локальными применительно к конструкциям, в которых они упоминаются. Например, рассмотрим переменную х, используемую в следующих двух правилах (defrule example-1 (data-1 ?x) => (printout t "х = " х crlf) ) (defrule example-2 (data-2 ?x) — > (printout t "х = " х Значение переменной ? х в правиле example — 1 не ограничивает каким-либо образом значение переменной х в правиле example — 2. Если в список фактов будет внесен факт — 1 3), то переменной ? х в правиле example-1 будет присвоено значение 3. Но это не означает, что тем самым на шаблон правила example — 2 будет наложено такое ограничение, что он должен согласовываться только с таким фактом data-2, в котором значение переменной ?x равно Более того, внесение в список фактов еще одного факта, например (data — 1 4), не приведет к появлению такого ограничения значениях, что оно должно совпадать со значениями двух активизаций правила example — 1. По существу, каждое частичное соответствие или каждая активизация правила имеет свое собственное множество локальных переменных. Тоже относится к каждому вызову конструкции de f function. Глобальные переменные определяются с помощью конструкции defglobal. Конструкция de f global имеет следующий общий формат (defglobal [ 10.4. Конструкция de f global 803 бальные переменные помещаются в текущий модуль. Имена глобальных переменных начинаются и заканчиваются знаком *, поэтому можно легко определить, что ? х является локальной переменной, а *х* представляет собой глобальную переменную. В определении конструкции de f global начальное значение для каждой переменной de f global задается путем вычисления выражения s s ion> и присваивания полученного значения переменной de f global, например, как показано ниже. CLIPS> (defglobal х 3 ух Обратите внимание на то, что мы смогли использовать значение глобальной переменной х для вычисления начального значения глобальной переменной у, поскольку переменная х определена прежде, чему. Кроме того, глобальные переменные сохраняют свои значения за пределами всех конструкций, поэтому достаточно просто ввести имя глобальной переменной в приглашении к вводу команды, чтобы определить ее значение. Ссылки на глобальные переменные de f global могут использоваться не только в командной строке, но и везде, где может быть задано любое выражение Кроме всего прочего, это означает, что такие ссылки могут использоваться в теле любой конструкции def function ив правой части любого правила. Но если указанные ссылки содержатся в вызове функции, то их нельзя применять в качестве параметра конструкции def function и допускается их использование только в левой части некоторого правила. Например, все приведенные ниже конструкции являются недопустимыми. (deffunction х уху х) =>) (defrule illegal- 3 (data-1 х) =>) 804 Глава 10. Процедурное программирование Команды, применяемые наряду с конструкциями defglobal Значение переменной de f global можно изменить с помощью команды bind. Достаточно просто вместо указания локальной переменной указать глобальную переменную. Для манипулирования конструкциями defglobal предусмотрено несколько команд. Для вывода на внешнее устройство текстового представления конструкции deйд1оЬа1 используется команда сокращение от pretty print defglobal — структурированный вывод конструкции бейд1оЬа1). Для удаления конструкции с1ейд1оЬа1 применяется команда undefglobal. Для отображения списка конструкций с1ейд1оЬа1, которые определены в системе служит команда list-defglobals. А для отображения имени значений переменных de f global, которые определены в системе, применяется команда show-defglobals. Функция get- defglobal-list возвращает многозначное значение, содержащее список конструкций de f global. Эти команды имеют следующий синтаксис (ppdefglobal [ (ppdefglobal у) (defglobal МАХИ ух Х У For ах уху х = 3 805 10.4. Конструкция de f global у = 5 CLIPS> (undefglobal у (list-defglobals)J Ха. Принципы сброса (переустановки) значения переменной defglobal Такой применяемый по умолчанию принцип переустановки значений глобальных переменных можно отменить путем вызова функции set-reset-globals с параметром, имеющим значение FALSE, а после передачи в эту функцию значения TRUE указанный принцип переустановки снова вступает в действие. Если применяемый по умолчанию принцип переустановки значений глобальных переменных отменен, то команда reset не восстанавливает значения переменных de f global таким образом, чтобы они принимали первоначальное значение, заданное в определении, как показано ниже. Значение переменной de f global восстанавливается, принимая первоначальное значение, заданное в определении, после выдачи каждой команды reset или использования команды bind для изменения значения глобальной переменной в такой форме, в которой не задано новое значение, как показано в следующем примере CLIPS> (bind х зове) J some-value CLIPS> ?*x*J some-value CLIPS> (reset) J CLIPS> ?*x*J 3 CLIPS> (bind х another-value)J another-value CLIPS> ?*x*J another- value CLIPS> (bind ?*x*)J 3 CLIPS> ?*x*J 3 CLIPS> 806 (bind ?*x* 5) 1 (set-reset-globals FALSE)J (reset)J ?*x*J (set- reset-globals TRUE)J (reset)J ?*x*J Отслеживание значений переменных de f global Если с помощью команды watch осуществляется отслеживание значений переменных то при каждом изменении значения любой переменной бейд1оЬа1 выводится информационное сообщение, например, как показано ниже. Знаки вначале информационного сообщения указывают на то, что глобальной переменной присвоено значение. Следующий символ представляет собой имя модифицируемой переменной бейд1оЬа1. За ним следуют знаки >, после чего показано новое значение глобальной переменной. За этим значением следуют знаки < —, а после показано старое значение глобальной переменной. CLIPS> 5 CLIPS> TRUE CLIPS> CLIPS> 5 CLIPS> FALSE CLIPS> CLIPS> 3 CLIPS> CLIPS> (watch globals)J С (bind ?*x* 6) 1 — х > б 3 б CLIPS> (bind х 7) ! — х > 7 < б 7 CLIPS> (reset)J — ? *x* > 3 < 7 CLIPS> (unwatch globals)J CLIPS> (bind х 8) 1 8 CLIPS> Глава 10. Процедурное программирование. Конструкция de f global 807 Переменные de f global и сопоставление с шаблонами Переменные defglobal могут использоваться в выражениях в левых частях правил, но изменения значений переменных defglobal не активизируют сопоставление с шаблонами. Например, рассмотрим следующие конструкции defglobal и defrule: (defglobal ?*z* = 4) (defrule global-example (data ?z&:(> ?z ?*z*)) =>) Теперь проанализируем, что произойдет после внесения в список фактов таких фактов data, которые согласуются с единственным шаблоном в правиле global-example: CLIPS> (reset)J CLIPS> ? *z*J 4 CLIPS> (assert (data 5) (data б) 1 f-0 (initial-fact) f-1 (data 5) f-2 (data б) For а total of 3 facts. CLIPS> (agenda)J 0 global-example: f-1 0 global-example: f-2 For а total of 2 activations. CLIPS> После внесения факта (data 5) в список фактов значение 5 связывается с локальной переменной ?z, а затем это значение сравнивается со значением глобальной переменной ?*z*, которое равно 4. Поскольку 5 больше, чем то правило global — example активизируется с помощью факта f — 1. Аналогичным образом, поскольку б больше, чем 4, то правило активизируется также под действием факта f — 2. К этому моменту сопоставление с шаблонами закончено и изменение значения переменной ? г не приводит к повторному вычислению шаблона применительно к активизациям, сформированным для данного правила, как показано ниже (bind х 5) 1 5 CLIPS> (Глава 10. Процедурное программирование 808 0 global-example: f-1 0 global-example: f-2 For а total of 2 activations. CLIPS> После извлечения и последующего внесения в список фактов того же факта (data 5) с индексом f — 1 условие правила global — example больше не удовлетворяется под действием этого факта, поэтому активизация не формируется. Но внесение факта ( data 7 ) в список фактов вызывает активизацию, поскольку значение переменной ? z равно 7 и поэтому больше нового значения глобальной переменной ? *z*, равного 5. Использование конструкций de f global Переменные defglobal в наибольшей степени подходят для применения в правилах в качестве констант кроме того, глобальные переменные могут служить для передачи информации, которая используется только в правой части правила и должна активизировать сопоставление с шаблонами. Переменные defglobal, применяемые в качестве констант, позволяют упростить понимание программы. Рассмотрим следующее правило Изменение значения переменной *z*, которое становится равным 5, не приводит к удалению активизации, вызванной действием факта f — 1, даже несмотря на то, что значение 5, находящееся в факте data и согласованное с правилом global — example, больше не превышает значение, хранящееся в глобальной переменной ? *z*. Но извлечение этого факта и последующее его повторное внесение вызывает повторную активизацию процесса сопоставления с шаблонами и применение нового значения глобальной переменной, также, как и внесение любого нового факта в список фактов CLIPS> (retract 1) 1 CLIPS> (assert (data 5)) 1 1 activation. CLIPS> (assert (data 7)) 1 0 global-example: f-2 0 global-example: f-4 For а total of 2 activations. CLIPS> 10.4. Конструкция de йд1оЬа1 809 (defrule plant-advisory (temperature ?value Fahrenheit) (test (<= ?value 32)) > (printout t "It' s freezing." crlf) (printout t "Bring your plants inside." Безусловно, большинство людей знают, что вода замерзает при температуре О' по Цельсию, или при 32' по Фаренгейту, поэтому легко понять, почему так важна константа 32 в правиле plant — advisory. Ноне все знают, почему важны также другие константы. Поскольку значение 32 может быть присвоено глобальной переменной, то появляется возможность воспользоваться осмысленным символическим именем в правиле plant — advisory, чтобы смысл этого правила было легче понять ?*water-freezing-point-Fahrenheit* = 32) (defrule plant- advisory (temperature ?value Fahrenheit) (test (<= ?value ?*water- freezing-point-Fahrenheit*)) — > (printout t "It' s freezing." crlf) (printout t "Bring your plants inside. crlf)) Одно из направлений использования переменных defglobal для предотвращения активизации сопоставления с шаблонами состоит в осуществлении отладки. Предположим, что требуется вывести некоторую дополнительную информацию, кроме той, что предоставляется командой watch. Один из способов выполнения этой задачи состоит во введении команды printout или format вправила примерно по такому принципу (defrule debug-example (data х) => (printout t "Debug-example х = " х crlf)) Но при этом возникает проблема, связанная стем, что такие отладочные сообщения могут не требоваться постоянно, поэтому команды их вывода либо потребуется удалять, либо обозначать комментариями на то время, когда эти команды не должны действовать. Но более удобный способ решения этой задачи состоит в том, что логическое имя устройства, в которое направляется отладочный вывод, можно сохранить в переменной def global. Если в качестве параметра с логическим именем в команде printout или format используется nil, то вывод не формируется (хотя команда format все еще передает отформатированную строку в качестве возвращаемого значения. Этим свойством указанных команд можно Глава 10. Процедурное программирование воспользоваться, чтобы уничтожать отладочную информацию, когда вывод ее не требуется. Ниже показано, как откорректировать правило debug — example для использования в нем переменной de f global. (defglobal ?*debug-print* = nil) (defrule debug-example (data х) — > (printout ?*debug-print* "Debug-example х = " х crlf)) По умолчанию отладочная информация передается в устройство с логическим именем nil, поэтому вывод команды printout не отображается на экране. А для того чтобы обеспечить вывод отладочной информации на экран, достаточно задать символ t в качестве значения глобальной nepeMeHHQH? *debug-print*, как показано в следующем диалоге CLIPS> (reset)J CLIPS> (assert (data асах х = с Обратите внимание на то, что в этом примере вывод отладочной информации был разрешен после того, как была сформирована одна из активизаций правила debug — А если бы отладочная информация была сохранена в виде факта, то возможность сделать также не была бы предоставлена. В таком случае правило debug — example должно было выглядеть следующим образом (defrule debug- example (debug-print ?debug-print) (data х. Конструкции de f generic их" х crlf)) Извлечение факта debug — print и его последующее внесение в список фактов с новым значением привели бык повторной активизации всех тех активизаций правила debug — example, которые уже были запущены, а это может оказаться весьма нежелательным. Конструкции defgeneric и defmethod (defgeneric собой группу взаимосвязанных функций (называемых методами, в которых совместно применяется общее имя. В действительности универсальная функция, которая включает больше одного метода, называется перегруженной (поскольку в принципе предоставляет возможность ссылаться больше чем на один метод. Каждый метод в группе имеет свою собственную сигнатуру сигнатура характеризует количество и типы параметров, принимаемых методом. Вовремя обработки вызова универсальной функции в системе CLIPS анализируются параметры и вызывается на выполнение метод с сигнатурой, соответствующей параметрам, если есть таковой. Этот процесс известен под названием вызова перегруженного метода (generic dispatch). Для определения общего имени универсальной функции, используемого группой методов, служит конструкция defgeneric. Конструкция deйденегс имеет следующий общий формат Глава 10. Процедурное программирование [ который может использоваться для ссылки на метод. Предусмотрена также возможность присвоить методу определенный индекс, задав значение значение которая характеризуется использованием круглых скобок, за однозначной переменной следуют от нуля и больше обозначений типов, а затем представлен необязательный запрос. Допустимыми значениями для термов FLOAT, NUMBER, INSTANCE-NAME, INSTANCE-ADDRESS, INSTANCE, FACT — ADDRESS и EXTERNAL — Значением снова к конструкции de f function с именем check — которая рассматривалась в разделе 10.3. Первоначальным определением этой конструкции было следующее (deffunction check-input (?question $?values) (printout t ?question " " ?values " ") (bind ?answer (read)) (while (not (member$ ?answer ?values)) |