Дефиниране на функции в clips нови потребителски функции в clips се дефинират чрез конструкцията deffunction



Дата23.03.2017
Размер88.28 Kb.
ДЕФИНИРАНЕ НА ФУНКЦИИ В CLIPS

Нови потребителски функции в CLIPS се дефинират чрез конструкцията deffunction. Тази конструкция включва пет елемента: име на функцията; незадължителен коментар; задължителни параметри на функцията (списък от 0, 1 или повече задължителни параметри); незадължителен параметър на функцията (wildcard параметър) – означава се с променлива, предшествана от знака $; тяло на функцията – поредица от действия или изрази, които се изпълняват при обръщение към функцията.

Синтаксис:

(deffunction []

(* [])

*)

Потребителските функции трябва да имат уникални имена, различни от тези на вградените функции и генеричните функции. Всяка функция трябва да бъде дефинирана преди да бъде използвана (извикана) от друга функция, метод на генерична функция, правило и др. Изключение правят само функциите, които съдържат пряка рекурсия.

Във всяко обръщение към дадена потребителска функция трябва да се включат поне толкова фактически параметри, колкото е броят на задължителните параметри в дефиницията на функцията. Незадължителният параметър от дефиницията на функцията се свързва със списъка от оценките на останалите фактически параметри.

Пример


> (clear)

> (deffunction print-args (?a ?b $?c)

(printout t ?a ″ ″ ?b ″ and ″ (length ?c) ″ extras: ″ ?c crlf))

> (print-args 1 2)

1 2 and 0 extras: ()

> (print-args a b c d)

a b and 2 extras: (c d)

При обръщение към потребителска функция действията от тялото й се изпълняват последователно. Ако тялото на дефиницията не съдържа действия (т.е. съдържа 0 действия), функцията връща стойност FALSE. В противен случай функцията връща оценката на последното изпълнено действие (оценката на последния оценен израз). Ако се установи грешка по време на изпълнение, изпълнението се прекратява веднага (и неизпълнените до момента действия се отхвърлят) и функцията връща резултат FALSE.

Дефинираните от потребителя функции могат да съдържат пряка или косвена рекурсия.

Пример за дефиниция на функция, която съдържа пряка рекурсия:



(deffunction factorial (?n)

(if (or (not (integerp ?n)) (< ?n 0))

then (printout t ″Factorial error!″ crlf)

else (if (= ?n 0) then 1 else (* ?n (factorial (- ?n 1))))))

Косвената рекурсия между две функции изисква специална forward декларация на едната от тях. Forward декларацията включва само заглавната част на дефиницията на функцията (без тялото й).

Пример:

(deffunction f2 ())

(deffunction f1 () (f2))

(deffunction f2 () (f1))

Забележка. Трябва да се внимава с дефинирането на рекурсивни функции, тъй като прекалено дълбоката рекурсия може да доведе до препълване на системния стек (особено при PC-type компютри).




ГЕНЕРИЧНИ ФУНКЦИИ

С помощта на конструкциите defgeneric и defmethod могат да се дефинират т. нар. генерични (generic; родови) функции на CLIPS.

Генеричните функции са по-мощни от “обикновените” функции, дефинирани от потребителя, тъй като с тяхна помощ може да се извършват различни действия в зависимост от типовете (класовете) на аргументите и техния брой. Например, операторът “+”, който е вграден (и реализира събирането на числа), може да бъде додефиниран за извършване на конкатенация на символни низове. Генеричните функции са съставени от множество компоненти, наречени методи. Всеки метод е валиден за някой от различните възможни случаи за аргументите на генеричната функция. За генеричните функции, които имат повече от един метод, се казва, че те (техните методи) се додефинират (overloaded).

Генеричните функции могат да имат като имплицитни (неявни) методи вградени функции на CLIPS или външни функции, дефинирани от потребителя. Например, едно додефиниране (overloading) на вградената функция (оператора) “+” с цел обхващането и на символни низове може да включва два метода: един неявен метод, който е вградената функция за числено събиране; един явен (explicit, дефиниран от потребителя) метод, описващ случая на конкатенация на символни низове.

Дефинирани от потребителя функции на CLIPS (т.е. функции, дефинирани чрез deffunction) не могат да бъдат методи на генерични функции.

В повечето случаи методите на генеричните функции не се изпълняват (цитират) директно. Изключение в това отношение е функцията call-specific-method. В общия случай CLIPS “познава”, че се прави обръщение към генерична функция, и използва аргументите на обръщението, за да намери и изпълни подходящия метод. Този процес се нарича generic dispatch.



Дефиниране на нови генерични функции


Дефиницията на една генерична функция включва заглавна част (header), която е подобна на forward декларация, и 0 или повече метода. Заглавната част на генеричната функция може или да е явно (експлицитно) дефинирана от потребителя, или да е декларирана неявно чрез дефиницията на поне един метод. Дефиницията на всеки метод се състои от 6 елемента: име (което определя на коя генерична функция принадлежи методът); незадължителен индекс (число); незадължителен коментар (низ); множество от ограничения за задължителните параметри; ограничения за незадължителния (wildcard) параметър, който осигурява възможност за работа с променлив брой параметри; поредица от действия (или изрази), които представляват тялото на метода (които се изпълняват при извикване на метода).

Ограниченията за параметрите се използват от generic dispatch-а за определяне на приложимостта на метода върху дадено множество от параметри. За определяне на заглавната част на дадена генерична функция се използва конструкцията defgeneric, а за дефиниране на всеки от методите се използва конструкцията defmethod.

Синтаксис:

(defgeneric [])

(defmethod [] []

(
* [])


*)


::=


|

( * [])

::=

|

( * [])

Ограничението по тип (върху типа) определя множеството от допустимите класове, на които може да принадлежи съответният фактически параметър:



::=

Допълнителното (query) ограничение представлява булев тест, дефиниран от потребителя, който трябва да бъде удовлетворен от съответния фактически параметър:



::= |

Всяка генерична функция трябва да бъде дефинирана (или чрез заглавна част, или чрез поне един метод) преди да бъде извикана от метод на друга генерична функция, от потребителска функция, правило и т.н. Единствено изключение са генеричните функции, съдържащи пряка рекурсия.

Примерна дефиниция:

> (clear)

> (defmethod + ((?a STRING) (?b STRING))

(str-cat ?a ?b))

> (+ 1 2)

3

> (+ ″ala″ ″bala″)

alabala″



>

С помощта на горната дефиниция + става генерична функция с два метода:



  • имплицитен (неявен) – вграденото числено събиране;

  • експлицитен (явен) – конкатенацията на низове.

Един метод се идентифицира еднозначно или чрез името и индекса си, или чрез името и ограниченията върху параметрите си. Всеки метод на дадена генерична функция получава в момента на дефинирането си уникален индекс сред множеството от методите на същата генерична функция. Ако при дефинирането на даден метод е посочен явно индекс, който съвпада с индекса на съществуващ метод, то новият метод замества стария. Ако новият метод има същото име и същите ограничения върху параметрите, както по-рано дефиниран метод, то новият метод замества стария. Всяка разлика в ограниченията върху параметрите обаче предизвиква добавяне на новия метод към множеството от съществуващите такива.

Един метод е приложим към дадено множество от фактически параметри, ако всеки от тези параметри удовлетворява съответните ограничения, описани в дефиницията на метода.

Най-напред се проверява дали съответният фактически параметър удовлетворява ограниченията по тип. След това се проверява дали той удовлетворява съответното допълнително (query) ограничение.

Даден метод може да получи точно (или поне) толкова фактически параметри, колкото е броят на задължителните параметри в неговата дефиниция. Ако в дефиницията е включен и незадължителен параметър, тогава методът може да получи и по-голям брой фактически параметри, като незадължителният параметър се свързва с множествена стойност, която е съставена от оценките на допълнителните фактически параметри.

Ограниченията върху типа и допълнителните ограничения се проверяват за всяко от полетата на множествената стойност, с която се свързва евентуалният незадължителен параметър (а не за цялата множествена стойност). Допълнителното (query) ограничение не се проверява, ако съответната множествена стойност е празна (съдържа 0 полета).

За цитиране на текущия елемент (текущото поле) на незадължителния параметър може да се използва специалната променлива ?current-argument. Тази променлива може да бъде включвана само в допълнителното (query) ограничение; тя няма смисъл извън тялото на метода.

Пример:

> (clear)

> (defmethod + (($?any INTEGER (evenp ?current-argument)))

(div (call-next-method) 2))

> (+ 1 2)

3

> (+ 4 6 4)

7

Изпълнение на генеричните функции


Когато бъде извикана една генерична функция, CLIPS избира най-специфичния (най-приоритетния) от методите на тази функция, които са приложими към съответните фактически параметри, и го изпълнява. Полученият резултат се връща като оценка на обръщението към генеричната функция.
Правила за намиране на по-приоритетния (по-специфичния)

от два приложими метода

Ограниченията върху параметрите на двата метода се сравняват от ляво на дясно. Валиден е резултатът от сравнението на първата срещната двойка параметри, при което се специфицира предшестването (приоритетът).

Правилата 1-3 са за сравнение между двойка съответни параметри, а правилата 4-6 са за глобално сравнение (в случай, че първите три не дадат резултат):


  1. По-специфичното ограничение по тип (върху типа) за даден параметър има приоритет.

  2. Параметър с query ограничение има по-висок приоритет от такъв без query ограничение.

  3. Задължителен параметър има приоритет пред незадължителен.

  4. Методът с по-голям брой задължителни параметри е по-приоритетен.

  5. Метод, който няма незадължителен параметър, е по-приоритетен от метод, който има незадължителен параметър.

  6. Метод, който е дефиниран преди друг метод, има приоритет.

Ако в съответните ограничения върху типа на параметрите участват по няколко класа, тогава определянето на относителната специфичност на ограниченията е по-трудна задача. Ще посочим някои прaвила, които определят наредбата между два списъка от класове. Двата списъка от класове се сравняват поелементно от ляво на дясно. Първата двойка, която съдържа клас и негов подклас, определя наредбата: списъкът от класове, който съдържа подкласа, има приоритет. Ако няма двойка съответни класове, които да определят наредбата между двата списъка, тогава по-късият списък от класове има приоритет. Ако и този критерий не даде резултат, тогава на базата на разглежданите списъци от класове не може да се определи приоритетът между ограниченията върху параметрите.

Пример 1


(defmethod f ((?a NUMBER STRING))) ;;; #1

(defmethod f ((?a INTEGER LEXEME))) ;;; #2

Ред: #2, #1 (INTEGER е подклас на NUMBER).

Пример 2

(defmethod f ((?a MULTIFIELD STRING))) ;;; #1

(defmethod f ((?a LEXEME))) ;;; #2

Ред: #2, #1 (MULTIFIELD и LEXEME са несравними (unrelated), #2 има по-малко елементи в списъка на класовете).

Пример 3

(defmethod f ((?a INTEGER LEXEME))) ;;; #1

(defmethod f ((?a STRING NUMBER))) ;;; #2

Ред: #1, #2 (съответните двойки от класове на несравними; определящ е редът на дефиниране на двата метода).



Забележка. Класът LEXEME е дефиниран като обединение на SYMBOL и STRING.

Пример за дефиниция на генерична функция с множество методи:



; Системният оператор + е неявен метод на функцията.

; Неговата дефиниция, поддържана от системата, е:

; (defmethod + ((?a NUMBER) (?b NUMBER) ($?rest NUMBER))) ; #1

(defmethod + ((?a NUMBER) (?b INTEGER))) ; #2

(defmethod + ((?a INTEGER) (?b INTEGER))) ; #3

(defmethod + ((?a INTEGER) (?b NUMBER))) ; #4

(defmethod + ((?a NUMBER) (?b NUMBER) ($?rest PRIMITIVE))) ; #5

(defmethod + ((?a NUMBER) (?b INTEGER (> ?b 2)))) ; #6

(defmethod + ((?a INTEGER (> ?a 2)) (?b INTEGER (> ?b 3)))) ; #7

(defmethod + ((?a INTEGER (> ?a 2)) (?b NUMBER))) ; #8

Редът на приоритет (специфичност) на методите в случая е: #7, #8, #3, #4, #6, #2, #1, #5.



Конструкции за работа с генерични функции


(get-defgeneric-list)

Връща списък от имената на всички генерични функции, които са дефинирани до момента.



(get-defmethod-list [])

Връща списък от дефинициите на всички дефинирани методи (за дадена генерична функция).



(list-defmethods [])

Връща списък от всички дефинирани методи (за дадена генерична функция). Елементите на този списък имат вида:



<име на функция> <индекс> <ограничения върху параметрите>

(call-next-method)

Извиква следващия по специфичност метод, който е приложим към същите фактически параметри, които е получил текущо изпълняваният метод.



(override-next-method *)

Тази функция е подобна на предишната, но тук се използват нови фактически параметри. Измежду методите, които са по-общи от текущо изпълнявания, се избира и изпълнява най-специфичният метод, който е приложим към новите фактически параметри.

Пример:

> (clear)

> (defmethod + ((?a INTEGER) (?b INTEGER))

(override-next-method (* ?a 2) (* ?b 3)))

> (list-defmethods +)

+ #2 (INTEGER) (INTEGER)

+ #SYS1 (NUMBER) (NUMBER) ($? NUMBER)

> (+ 1 2)

8

(call-specific-method *)

Тази функция позволява да бъде изпълнен конкретен метод (който се задава чрез неговия индекс), без да има значение неговата специфичност. Посоченият метод трябва да бъде приложим към дадените фактически параметри.



Пример:

> (clear)

> (defmethod + ((?a INTEGER) (?b INTEGER))

(* (- ?a ?b)(- ?b ?a)))

> (+ 1 2)

-1

> (call specific-method + 1 1 2)

3


Поделитесь с Вашими друзьями:


База данных защищена авторским правом ©obuch.info 2019
отнасят до администрацията

    Начална страница