Функция, создаваемая с помощью конструктора deffunction, должна иметь уникальное имя, не совпадающее с именами других внешних и внутренних функций. Функция, созданная с помощью deffunction, не может быть перегружена (см. гл. 10). Конструктор deffunction должен быть объявлен до первого использования создаваемой им функции. Исключения составляют только рекурсивные функции.
В зависимости от того, задан ли групповой параметр, функция, созданная конструктором, может принимать точное число параметров или число параметров не меньшее, чем некоторое заданное. Обязательные параметры определяют минимальное число аргументов, которое должно быть передано функции при ее вызове. В действиях функции можно ссылаться на каждый из этих параметров как на обычные переменные, содержащие простые значения. Если был задан групповой параметр, то функция может принимать любое количество аргументов большее или равное минимальному числу. Если групповой параметр не задан, то функция может принимать число аргументов точно равное числу обязательных параметров. Все аргументы функции, которые не соответствуют обязательным параметрам, группируются в одно значение составного поля. Ссылаться на это значение можно, используя символ группового параметра. Для работы с групповым параметром могут использоваться стандартные функции CLIPS, предназначенные для работы с составными полями (см. гл. 15), такие как length и nth. Определение функции может содержать только один групповой параметр.
Приведенный пример 8.1 демонстрирует описанные выше возможности работы с групповыми параметрами.
Пример 8.1. Использование группового параметра
(deffunction print-args (?a ?b $?c)
(printout t ?a “ “ ?b “ and “ (length ?c) “ extras: “ ?c
crlf))
(print-args 1 2)
(print-args a b c d)
(print-args a)
В данном примере с помощью конструктора deffunction определяется функция print-args, которая принимает два обязательных параметра: ?а и ?b, и имеет групповой параметр $?с. Функция выводит на экран свои обязательные параметры, а также число полей в составном параметре и его содержимое. Результат выполнения данного примера приведен на рис. 8.1.
Рис. 8.1.Результат работы функции print-args
Обратите внимание, что вызов функции с числом параметров, меньшим минимального, приводит к сообщению об ошибке.
При вызове функции интерпретатор CLIPS последовательно выполняет действия в порядке, заданном конструктором. Функция возвращает значение, равное значению, которое вернуло последнее действие или вычисленное выражение. Если последнее действие не вернуло никакого результата, то выполняемая функция также не вернет результата (как в приведенном выше примере). Если функция не выполняет никаких действий, то возвращенное значение равно false. В случае возникновения ошибки при выполнении очередного действия выполнение функции будет прервано и возвращенным значением также будет false.
Функции могут быть само- и взаимно рекурсивными. Саморекурсивная функция просто вызывает сама себя из списка своих собственных действий. В качестве примера можно привести функцию, вычисляющую факториал.
Пример 8.2. Использование рекурсии для вычисления факториала
(deffunction factorial (?a)
(if (or (not (integerp ?a)) (< ?a 0)) then
(printout t "Factorial Error!" crlf)
else
(if (= ?a 0) then
else
( * ?a (factorial (- ?a 1))))))
Взаимная рекурсия между двумя функциями требует предварительного объявления одной из этих функций. Для предварительного объявления функции в CLIPS используется конструктор deffunction с пустым списком действий. В следующем примере функция foo предварительно объявлена и таким образом может быть вызвана из функции bar. Окончательная реализация функции foo выполнена конструктором после объявления функции bar.
Пример 8.3. Создание взаимно рекурсивных функций
(deffunction foo ())
(deffunction bar ()
(foo))
(deffunction foo ()
(bar) )
Внимательно следите за рекурсивными вызовами функций, слишком большой уровень рекурсии может привести к переполнению стека памяти. Например, приведенный выше пример с функциями bar и foo приводит к результату, представленному на рис. 8.2, и аварийному завершению CLIPS.
Рис. 8.2.Переполнение стека
Обратите внимание, что на рис. 8.2 в главном окне CLIPS выводится информация о запуске каждой функции. Для установки этого режима воспользуйтесь диалоговым окном Watch Options.Для этого откройте диалоговое окно, выбрав пункт Watchиз меню Execution,и установите флажок Deffunctions,как показано на рис. 8.3. Этот режим также позволяет просматривать аргументы, которые использовались при каждом конкретном вызове функции.
Рис. 8.3.Установка режима просмотра вызова функций
Так же как и для правил, предопределенных фактов, глобальных переменных Windows-версия CLIPS предоставляет специальный инструмент для работы с функциями — Deffunction Manager(Менеджер функций). Для запуска этого инструмента воспользуйтесь пунктом Deffunction Managerиз меню Browse. Вслучае если в CLIPS не определена ни одна функция, данный пункт меню недоступен. Менеджер функций, отображающий функции, созданные нами в этой главе, изображен на рис. 8.4.
Рис. 8.4.Окно менеджера функций
Общее количество внешних функций отображается в заголовке окна менеджера — Deffunction Manager — 4 Items.С его помощью можно распечатать определение функции, удалить ее, а также установить режим просмотра вызова для отдельно выбранной функции.