Sunteți pe pagina 1din 98

Prefaţă

Lucrarea este un îndrumar de lucrări practice în domeniul Programării bazate pe reguli.


Aceasta a rezultat în urma orelor de laborator şi proiect desfăşurate la o serie de discipline din
domeniul Inteligenţei artificiale, şi anume: Sisteme bazate pe cunoaştere, Reprezentarea
cunoaşterii şi raţionament, Inteligenţa artificială, Sisteme de inteligenţă artificială distribuite.
Acestea au fost şi sunt susţinute în cadrul Departamentului de Automatică şi Informatică
Aplicată, Facultatea de Automatică şi Calculatoare de la Universitatea Tehnică „Gheorghe
Asachi” din Iaşi. Programarea bazată pe reguli şi în particular limbajul CLIPS, cel tratat în
prezentul îndrumar, reprezintă un instrument care permite materializarea a diferite metode din
Inteligenţa artificială. Se explică astfel felul în care aplicaţii dezvoltate în CLIPS au fost tratate în
cadrul mai multor obiecte de studiu, atât din programul de licenţă Automatică şi Informatică
Aplicată, din domeniul Ingineriei sistemelor, cât şi la programe de masterat ce au inclus obiecte
din aria Inteligenţei artificiale.
Fără a fi o tratare exhaustivă, lucrarea cuprinde aspectele esenţiale pentru înţelegerea
programării bazate pe reguli, în varianta în care motorul de inferenţe folosit este unul lucrând
după căutare înainte (aşa numitul mecanism ”forward chaining”). CLIPS este un reprezentant
semnificativ al clasei de limbaje folosind un motor de inferenţe cu căutare înainte. Acesta are
şi avantajul că este un mediu de programare liber, care după mai bine de 25 de ani de la creare
a ajuns să fie bine documentat şi validat în numeroase aplicaţii. Lucrarea este utilă în special
studenţilor care au ore de laborator şi proiect la disciplinele menţionate, dar şi în general celor
care vor să înveţe sau să-şi completeze cunoştinţele privind programarea bazată pe reguli.
Întrebarea care s-ar putea pune ar fi aceea dacă, în condiţiile unei adevărate explozii a
tehnicilor şi instrumentelor de programare, mai este de actualitate programarea bazată pe
reguli. Principala utilitate a acestei metode şi în particular a limbajului CLIPS este aceea
privind realizarea sistemelor expert. Acestea reprezintă unul din rezultatele semnificative ale
Inteligenţei artificiale, existând aplicaţii cu sisteme expert în domenii diferite (exemple
semnificative se regăsesc în industrie, medicină, domeniul financiar). În sprijinul ideii
utilităţii programării bazate pe reguli poate fi considerată eficienţa cu care metoda respectivă
poate rezolva anumite tipuri de probleme: cele nestructurate (greu de algoritmizat), cele care
implică o cunoaştere de tip logic. Ca un singur exemplu, una din marile companii din
domeniul sistemelor de control a lansat, în urmă cu un an, un nou sistem expert dedicat
optimizării producţiei de ciment; este vorba despre un domeniu în care folosirea unei variante
clasice, algoritmice, dacă nu este imposibilă, este oricum ineficientă în comparaţie cu soluţia
bazată pe reguli.

Volumul conţine un număr de nouă lucrări, organizate într-o succesiune în care se


parcurg mai întâi aspectele elementare, privind organizarea bazei de fapte, a celei de reguli,
realizarea mecanismului de potrivire într-un program bazat pe reguli şi se finalizează cu un
aspect de luat în considerare în probleme complexe, în care sunt necesare precauţii pentru
păstrarea corectitudinii bazei de cunoştinţe. Fiecare lucrare este însoţită de probleme propuse,
deprinderea programării bazate pe reguli, ca şi a oricărei alte variante de programare,
presupunând înţelegerea unor exemple şi rezolvarea de probleme.

Iaşi, martie 2013

Autorii

3
Cuprins

Lucrarea nr. 1. Domeniul sistemelor expert. Mediul CLIPS. Baza de fapte în


CLIPS ………………...…………….....................…………………………..5

Lucrarea nr. 2. Definirea regulilor în CLIPS ..……........…......……….……14

Lucrarea nr. 3. Procesul de potrivire în CLIPS; variabilele în CLIPS ..........24

Lucrarea nr. 4. Realizarea unui program de planificare a acţiunilor în CLIPS;


elemente de interdependenţă între baza de fapte şi baza de reguli ................35

Lucrarea nr. 5. Realizarea testelor în CLIPS ………................…………….42

Lucrarea nr. 6. Tehnici de intrare şi de ieşire în CLIPS; elemente procedurale


şi comenzi utilitare …………………..………………...................................63

Lucrarea nr. 7. Fapte CLIPS de tip structurat …………................………....77

Lucrarea nr. 8. Realizarea unui program de planificare a acţiunilor în CLIPS


în varianta nesimplificată ………………………………...............................89

Lucrarea nr. 9. Sistemul de menţinere a consistenţei bazei de fapte în CLIPS


........................................................................................................................93

Bibliografie ........................................………………………..….............100

4
LUCRAREA NR. 1

Domeniul sistemelor expert. Mediul CLIPS. Baza de fapte în CLIPS

1. Introducere în domeniul sistemelor expert

Un sistem expert (SE) este un program sau un sistem de programe care înmagazinând
cunoştinţe ale unor experţi umani poate rezolva probleme complexe dintr-un domeniu de
expertiză. În definiţia de mai sus expertul trebuie înţeles ca fiind o persoană ce are cunoştinţe
deosebite într-un anumit domeniu, adică este capabil să facă o expertiză în acel domeniu.
Aceasta înseamnă că expertul poate rezolva probleme pe care alţii nu le pot rezolva deloc, sau nu
le pot rezolva în mod eficient.
Arhitectura unui SE este prezentată în fig.1. Baza de cunoştinţe conţine două părţi care se
referă la cunoştinţe generale din domeniul de expertiză, aflate în baza de reguli şi respectiv la
cunoştinţe specifice problemei care se rezolvă, aflate în baza de fapte.

Intrări Baza de cunoştinţe Reprezentare


(fapte) a
Baza de fapte Baza de reguli cunoaşterii

Efectuare
Ieşiri Motor de inferenţe de
(expertiză) raţionamente
SE

Fig. 1. Structura unui sistem expert

O regulă este o afirmaţie de forma:

Dacă <parte de condiţie> atunci <parte de acţiune>

Partea de condiţie se mai numeşte şi parte stângă a regulii sau antecedent; partea de
acţiune se mai numeşte şi consecinţă (concluzie) sau parte dreaptă a regulii. Baza de fapte
conţine fapte, adică afirmaţii considerate adevărate la momentul respectiv pentru problema aflată
în lucru. Faptele trebuie să aibă aceeaşi formă ca şi componentele părţilor de condiţie ale
regulilor.
Un exemplu principial de regulă este:

Dacă “granulaţie-material este mare”


şi “curent-motor-grila1 este 10 A”
şi “mecanism-grila1 este în funcţiune” (R)
atunci “grosime-strat-ciment este mare”
şi “creşte-viteza motor-grila1”

De observat caracterul de generalitate al cunoştinţelor conţinute în regula (R); este vorba


despre o situaţie valabilă în general în cazul unui proces tehnologic din industria cimentului.
Dacă în baza de fapte se găsesc la un moment dat faptele care afirmă: “granulaţie-material este
mare”, “curent-motor-grila1 este 10A”, “mecanism-grila1 este în funcţiune”, atunci regula (R)
va fi activată, adică va fi selectată pentru declanşare (execuţie). Motorul de inferenţe este cel care
are rolul de a determina legăturile logice dintre reguli şi fapte; astfel, el identifică toate regulile

5
ce trebuie să fie activate la un moment dat şi apoi tot el selectează una singură dintre regulile
activate, pe care o pune în execuţie. Declanşarea unei reguli înseamnă punerea în aplicare a părţii
de acţiune, ceea ce poate avea unul sau mai multe efecte de genul modificării bazei de cunoştinţe
(de exemplu, primul element din partea de acţiune a regulii (R) determină adăugarea în baza
de fapte a faptului “grosime-strat-ciment este mare”), sau transmiterii de informaţii spre
exterior - spre operator şi/sau spre procesul condus (aşa cum este al doilea element din partea de
acţiune a regulii (R)). Mecanismul de funcţionare astfel explicat se referă la SE folosind un
motor de inferenţe cu căutare înainte (în limba engleză: ”forward chaining”).
Pentru ca SE să înceapă lucrul este necesar să primească în baza de fapte situaţia iniţial
valabilă pentru problema care urmează a fi rezolvată. De asemenea, mai ales în cazul unui SE
care conduce un proces industrial, intrările în baza de fapte apar şi în timpul rulării, atunci când
sunt actualizate unele fapte în conformitate cu schimbările din proces. Aceste intrări vor proveni
de la sistemul de achiziţie de cunoştinţe, subsistem care nu a fost reprezentat în fig. 1, dar care va
exista în cazul unor aplicaţii, asigurând una din posibilităţile de actualizare a bazei de cunoştinţe.
Dacă un program convenţional se limitează la a scoate la ieşire rezultatele problemei,
aceasta fiind rezolvată după algoritmul conţinut în program, un SE poate furniza la ieşire o
expertiză. Aceasta înseamnă că SE ia decizii în funcţie de problema pe care o rezolvă şi conduce
rezolvarea de fiecare dată într-un mod particular, în funcţie de situaţia specifică a problemei de
rezolvat. În plus, este posibil ca într-un sistem bazat pe cunoştinţe, ce conţine drept componentă
şi un SE, acesta să fie conectat cu un sistem de generare a explicaţiilor; pe baza rezultatelor
furnizate de SE şi urmărind ordinea de execuţie a regulilor, se explică raţionamentele prin care a
fost găsită soluţia furnizată. Aceasta este o deosebire importantă faţă de programarea
convenţională.
Există însă şi un alt aspect care se constituie într-o deosebire esenţială a unui program ce
este un SE faţă de un program convenţional. Este vorba despre separarea între cunoştinţele
domeniului problemei care se rezolvă şi elementele de control al programului (elementele care
ghidează rularea programului pe calculator). Această separare este netă în cazul unui SE, în
conformitate cu cele două componente ale sale - baza de cunoştinţe şi motorul de inferenţe:
• cunoştinţele din domeniul de expertiză apar numai în baza de cunoştinţe, fie în fapte sau în
reguli;
• elementele de control, care determină felul în care se produce rularea, se găsesc numai în
motorul de inferenţe; motorul de inferenţe conţine un algoritm generic, care este acelaşi,
indiferent de conţinutul bazei de cunoştinţe.
În programarea convenţională elementele de cunoaştere din domeniul problemei şi cele
de control al rulării programului sunt strâns întrepătrunse, neexistând o separare vizibilă. De
exemplu, în principiu, regula (R) s-ar putea scrie într-un limbaj de programare clasic sub forma:

If granulaţie-material == “mare” and curent-motor-grila1 == “10A”


and mecanism-grila1 ==“ în funcţiune”
then grosime-strat-ciment = mare” and “creşte-viteza-motor-grila1”

Deosebirea este aceea că momentul în care se execută instrucţiunea de mai sus este strict
dependent de locul unde este ea plasată în program; în contrast, într-un SE ordinea faptelor în
baza de fapte sau a regulilor în baza de reguli nu are nicio importanţă; ele devin active pe baza
mecanismului de control din motorul de inferenţe, care este complet separat de baza de
cunoştinţe. În schimb, în expresia de mai sus, considerată în programarea convenţională, sunt
amestecate cunoştinţe de control al rulării programului (cuvintele cheie if, then, and), cu acelea
din domeniul problemei (granulaţie-material = = “mare”).
Această separare a cunoştinţelor într-un SE atrage după sine şi alte deosebiri faţă de
programarea convenţională:
• stocarea cunoştinţelor domeniului problemei se face în cazul unui SE într-o formă uşor
accesibilă modificărilor, dezvoltărilor, actualizărilor; operatorul va acţiona numai în baza de

6
cunoştinţe, fără a interveni asupra motorului de inferenţe. În programarea clasică modificările
pot fi făcute de obicei numai de cel care cunoşte atât întreg algoritmul de rezolvare, cât şi
mecanismele de control ale limbajului folosit.
• atât ca rezultat al separării cunoştinţelor, cât şi prin folosirea unor tehnici adecvate de
reprezentare a cunoaşterii (capitol distinct al inteligenţei artificiale - IA), este posibilă
construirea bazei de cunoştinţe într-o formă declarativă (afirmaţii asupra a ceea ce este
adevărat la un moment dat). Din această formă declarativă, trecerea în forma procedurală, cea
necesară calculatorului, se face în mod automat, prin intermediul interpretorului sau
compilatorului mediului de programare destinat construirii SE. Aceasta este o facilitate atât
pentru programator, cât şi pentru utilizator, deoarece baza de cunoştinţe apare într-o formă
apropiată de limbajul natural. De exemplu, faptul care atestă că mecanismul grilei 1 este în
funcţiune poate fi scris în limbajul CLIPS, (unul din limbajele folosite pentru construirea SE),
în forma:

(mecanism-grila1 in-functiune)

Singurul element al sintaxei CLIPS sunt parantezele care delimitează faptul, conţinutul
acestuia putând fi ales într-un mod suficient de liber de programator, astfel încât să se apropie cât
mai mult de limbajul natural.

Pentru a realiza un SE există mai multe abordări în funcţie de instrumentele software de


care dispune programatorul. În principiu, trebuie realizate cele două componente care apar în fig.
1, adică baza de cunoştinţe (cu cele două subsisteme ale sale: baza de fapte şi baza de reguli) şi
motorul de inferenţe. Aceasta va fi situaţia în cazul în care programatorul foloseşte un limbaj de
programare general. Chiar şi în acest caz, în crearea SE sunt mai utilizate aşa zisele limbaje de
programare de IA, care introduc mai puţine restricţii de sintaxă şi sunt mai uşor de folosit în
realizarea bazei de fapte şi a celei de reguli (nu este totuşi imposibilă dezvoltarea unui SE în C,
de exemplu); un asemenea limbaj de IA este LISP (LISt Processing). Dezavantajul acestei
variante este acela că programatorul trebuie să construiască ambele componente ale SE. O
variantă mai bună din acest punct de vedere este aceea în care se foloseşte un mediu de
programare destinat SE, mediu care are deja motorul de inferenţe realizat, astfel că
programatorului îi rămâne să realizeze numai baza de fapte şi cea de reguli. Asemenea medii
sunt CLIPS, PROLOG. Dezavantajul în această a doua abordare este acela că utilizatorul nu
poate interveni asupra motorului de inferenţe decât în măsura în care mediul respectiv îi permite.
Totuşi, mai ales pentru folosirea SE în aplicaţii industriale, aceasta este varianta preferată,
deoarece motoarele de inferenţe realizate în diferite medii de programare au caracteristici (viteză
de lucru, memorie ocupată, siguranţă în funcţionare) pe care greu le poate obţine un programator
care îşi construieşte un motor de inferenţe propriu.

2. Prezentare generală a mediului de programare CLIPS

Mediul CLIPS este un instrument de programare destinat construirii SE. Denumirea este
un acronim de la “C Language Integrated Production System” (numele de sistem de producţie
este cel folosit iniţial pentru programarea bazată pe reguli, metodă din care s-au dezvoltat SE).
Câteva caracteristici ale CLIPS sunt:

• foloseşte un interpretor;
• conţine un motor de inferenţe bazat pe strategia înainte;
• foloseşte programarea bazată pe reguli, având şi un modul destinat programării
orientate pe obiecte;
• poate fi rulat atât sub sistemul de operare DOS, cât şi sub WINDOWS, UNIX, etc.
• conţine un depanator de programe.

7
3. Interacţiunea cu mediul CLIPS

O posibilitate de a interacţiona cu mediul CLIPS este de a transmite o comandă de la


nivel superficial (“top level”). Se ajunge la nivel superficial după lansarea interpretorul CLIPS,
prin comanda adecvată. De îndată ce a apărut prompterul “CLIPS>“, o comandă CLIPS poate fi
transmisă spre evaluare.
Comenzile CLIPS sunt de următoarele tipuri.

1. Apelul unei funcţii


Funcţiile se scriu în CLIPS în forma prefixată, fiecare apel al unei funcţii trebuind să fie
plasat într-o pereche de paranteze. Interpretorul evaluează funcţia respectivă, cu argumentele ei
şi afişează rezultatul evaluării.
De exemplu, calculul sumei 7 + 5 se va scrie în CLIPS:

(+ 7 5)

Observaţie
Dacă se dau mai multe comenzi una după alta, fară a da “Enter”, numai prima dintre ele
este luată în considerare.

2. Definiţia unei construcţii


Are drept efect crearea unei noi construcţii, care ţine seama de definiţia respectivă. Se
obţine astfel posibilitatea de construire a faptelor şi regulilor dintr-un SE.

3. Numele unei variabile globale


În CLIPS se pot folosi variabile globale; dacă utilizatorul transmite drept comandă
numele unei variabile globale, interpretorul determină valoarea acelei variabile la momentul
respectiv şi o afişează.

4. O constantă
Dacă utilizatorul transmite drept comandă o constantă, atunci interpretorul afişează
constanta respectivă.

Pentru a ieşi de sub interpretorul CLIPS se foloseşte comanda: (exit), sau din meniul
“File” comanda “Exit”.

4. Tipuri de constante (date primitive) în CLIPS

a) Număr
a1) Întreg - reprezentat intern ca un întreg C lung.
Exemple: 72; +53; -2.
a2) Real (float) - se reprezintă în dublă precizie; numărul de cifre semnificative
depinde de calculatorul pe care se află interpretorul. Se admite scrierea cu punct zecimal
şi cea cu mantisă şi exponent.
Exemple: -15.09; 273E3; -32.4e-4.

b) Simbol
Un simbol este o secvenţă de caractere ce începe cu un caracter ASCII tipăribil şi este
urmat de zero sau mai multe caractere ASCII tipăribile, cu următoarele excepţii:
un simbol nu va începe cu unul din caracterele: ~, <, |, &, $, ?, +, -, (, ), “, ;.
Explicaţia este legată de faptul că toate aceste caractere introduc situaţii speciale în CLIPS,
care vor fi explicate ulterior şi de aceea un simbol nu poate începe cu ele.

8
un simbol nu va putea conţine niciunul din caracterele: <, |, &, (, ), “, ~, ;. Toate
aceste 8 caractere, împreună cu spaţiul, tab-ul şi sfârşitul de linie, constituie delimitatori
(separatori) în CLIPS.
CLIPS face distincţie între literele mari şi cele mici.
Exemple de simboluri CLIPS: abc, A1, @2!?A.

c) Şir de caractere
Acesta este o succesiune de zero sau mai multe caractere, incluse între ghilimele. Dacă
vrem să includem într-un şir de caractere şi ghilimelele trebuie să folosim caracterul de control \
("backslash") înaintea ghilimelelor. Dacă un şir de caractere trebuie să conţină \ atunci acesta
trebuie dublat. Într-un şir de caractere poate apare şi spaţiul, şfârşitul şirului fiind marcat de
ghilimele.
Exemple de şiruri de caractere: “ab1”, “a:\\user\\a.clp”.

O dată primitivă CLIPS (număr, simbol sau şir de caractere) se numeşte şi câmp CLIPS
sau constantă CLIPS. Câmpurile CLIPS sunt separate prin delimitatori, care sunt cei amintiţi mai
sus. Atunci când utilizatorul introduce de la nivel superficial mai multe câmpuri succesiv, în
aceeaşi linie (fără a da "Enter"), interpretorul ia în considerare numai primul câmp.

5. Convenţii de notaţie

Pentru a înţelege sintaxa CLIPS facem câteva convenţii de notaţie; acestea nu sunt
elemente ale sintaxei CLIPS, ci doar convenţii care vor facilita înţelegerea limbajului.
Vom nota comanda “Enter” cu ¶.
Orice nu este inclus intre < >, [ ], { }, este un cuvânt cheie şi trebuie introdus ca
atare.
Perechea [ ] indică un element opţional.
Exemplu: (exemplul [1]). Această succesiune va putea fi scrisă în program (exemplul
1) sau (exemplul).
Perechea < > indică necesitatea substituirii cu un element (unul singur) de tipul indicat.
Exemplu: (exemplul <întreg>). O variantă corectă în program poate fi: (exemplul
21).
Succesiunea << >> indică necesitatea unei substituiri cu zero sau mai multe apariţii ale
unui element de tipul specificat. Dacă este vorba de mai multe apariţii, acestea vor fi separate
prin spaţiu.
Exemplu: (exemplul <<întreg>>). Variante corecte în program pot fi: (exemplul -2);
(exemplul 23 145 -3); (exemplul).
Succesiunea <<< >>> indică necesitatea unei substituiri cu una sau mai multe apariţii
de tipul specificat (nu zero apariţii).
Exemplu: (exemplul <<<întreg>>>). Variante corecte în program pot fi: (exemplul
20); (exemplul 23 145 -3).
Perechea { } indică faptul că unul singur din elementele dintre acolade va trebui să fie
folosit.
Exemplu: (exemplul {1, 2, abc}). Variante corecte în program pot fi: (exemplul 1);
(exemplul 2); (exemplul abc).

Exerciţii
1. Răspundeţi la întrebările:
• Ce este un SE şi care sunt componentele acestuia ?
• Care sunt deosebirile între un SE şi un program convenţional ?
• Care sunt cele două abordări privind construirea unui SE ?
• Care sunt tipurile de câmpuri CLIPS ?

9
2. Ştiind că operaţiile aritmetice se simbolizează în CLIPS prin +, -, *, / , că acestea
pot avea oricâţi operanzi, şi că nu există o prioritate a operaţiilor, rezolvaţi în CLIPS următoarele
calcule:
a) 25 + (17 - 2) : (4 + 1) x 10.
b) 17,3 x [15,5 : (13,2 - 1,5 - 10,3) : 3] x (100 : 2,14).

3. Precizaţi numărul de câmpuri din următoarele exemple:


a) ””abc”” b) -12”!vb|”ab+c c) ”\”a bc 12\” ”.

6. Construirea bazei de fapte în CLIPS


6.1 Forma faptelor

Un fapt CLIPS este o listă de unul sau mai multe câmpuri. În CLIPS o listă se
delimitează prin perechea de paranteze ( ); deci un fapt este unul sau mai multe câmpuri
(constante) incluse între paranteze. Conform convenţiilor de notaţie avem:

Fapt := (<<<câmp>>>).

Exemple de fapte:

(componente-calculator "cpu" memorie tastatura) (1)


(Ion este tatal-lui Vasile) (2)
(temperatura punct_A 123.5) (3)

Limbajul CLIPS, ca limbaj de IA, nu introduce restricţii în ceea ce priveşte felul în care
este organizat un fapt (numărul câmpurilor, ordinea acestora). Totuşi, la fel cum în limbajele
convenţionale există metode de organizare eficientă a programelor, tot aşa şi într-un program
CLIPS, în ceea ce priveşte baza de fapte, va fi de preferat o scriere a faptelor după anumite
tipare. Cum de obicei un fapt reprezintă o relaţie între mai multe elemente, relaţie adevărată la
momentul respectiv, este util să se respecte tiparul:

(<nume-relaţie> <<<elemente-aflate-în-relaţie>>) (*)

Folosirea unui acelaşi tipar pentru toate faptele din baza de fapte va determina uşurinţă în
urmărirea şi înţelegerea bazei de fapte de către utilizator şi programator, cât şi în actualizarea
acesteia. Faptele (1) şi (3) de mai sus respectă tiparul (*), în timp ce faptul (2) nu respectă acest
tipar; el poate fi rescris, conform tiparului (*), ajungându-se la forma:

(este-tatal-lui Ion Vasile) (4)

De remarcat numărul diferit de câmpuri din cele două forme (2) şi (4). Forma pe care o
stabilim pentru tiparele faptelor este importantă şi din punctul de vedere al corelaţiei care trebuie
creată în programarea bazată pe reguli între fapte şi reguli, aşa cum se va vedea ulterior. Ţinând
seama de această relaţie este important ca pe prima poziţie a faptelor să apară o valoare
cunoscută a priori (o valoare constantă), conform mecanismelor de potrivire a faptelor pe reguli
şi de efectuare a testelor în CLIPS, aspecte prezentate ulterior.
Concluzia este aceea că programatorul trebuie să stabilească tipare pe care, atât pentru
consecvenţă, cât şi pentru conceperea mai uşoară a regulilor, le vor urma faptele din baza de
fapte, tipare care trebuie să fie cât mai inteligibile şi de către beneficiarul SE. Din mai multe
considerente, inclusiv cele privind corelarea faptelor cu regulile, tiparele de forma (*) vor fi cele
preferate.

10
Mai există şi unele aspecte de optimalitate, referitoare la interdependenţa între forma
faptelor din baza de fapte şi regulile din baza de reguli, cât şi la ocuparea memoriei. Legat de
acest ultim aspect, se poate ca într-un fapt numele relaţiei să nu fie înscris într-o formă extinsă, ci
în una concisă, aceasta determinând o reducere a numărului de câmpuri, ca în exemplul următor;
de la faptul:

(este temperatura-aer-secundar 1000)

se ajunge la:

(temperatura-aer-secundar 1000).

Observaţie
Faptele introduse în acest paragraf se numesc fapte CLIPS nestructurate; există şi fapte
de tip structurat care au o sintaxă specifică. Ca o restricţie a sintaxei CLIPS, primul câmp dintr-
un fapt nestructurat trebuie să fie o constantă de tip simbol.

6.2 Adăugarea şi ştergerea faptelor din baza de fapte

Toate faptele cunoscute la un moment dat de interpretorul CLIPS sunt menţinute într-o
colecţie - baza de fapte (BF), care în CLIPS se mai numeşte şi listă de fapte (“fact list”) sau
memorie de lucru. Există mai multe posibilităţi de a introduce fapte în BF.

a) Introducerea faptelor de la nivel superficial sau dintr-o regulă prin comanda “assert”
Se pot introduce unul sau mai multe fapte în BF de la nivel superficial prin comanda:

(assert <<<fapt>>>)

Exemple de folosire a acestei comenzi sunt:

(assert (nume Ion) ) (5)


(assert (presiune punct_A 215) (temperatura punct_A 100.5) (stare alarma)) (6)

Se remarcă introducerea unui singur fapt în cazul (5) şi a trei fapte în cazul (6). Comanda
“assert” poate fi folosită nu numai la nivel superficial, ci şi în partea de acţiune a unei reguli, aşa
cum se va explica ulterior. Din punct de vedere al clasificării ce a fost dată în paragraful 3,
comanda "assert" va fi încadrată în categoria funcţiilor; "assert" este numele funcţiei, argumetele
sunt faptele care sunt delimitate de paranteze, iar efectul evaluării funcţiei este acela de
modificare a BF.
Faptele sunt prezente în BF împreună cu un identificator (adresă) care ajută la găsirea
acestora. Identificatorul este de forma: f - i, unde i este un contor, incrementat la fiecare
introducere a unui nou fapt în BF. Este de remarcat că, în cazul unor operaţii repetate de
introducere şi ştergere de fapte din BF, aceasta va rămâne cu “găuri”, în sensul incrementării
continue a contorului i, fără a se reveni la poziţiile din care au fost şterse fapte.
Utilizatorul are mai multe posibilităţi de a vedea starea BF la un moment dat. Astfel,
există comanda:

(facts [<start> [<end> [<maxim>] ] ] )

unde parametrii start, end şi maxim sunt întregi pozitivi. Semnificaţia este aceea că această
comandă poate fi folosită cu 0 până la 3 argumente. Dacă sunt 0 argumente atunci se afişează
întreaga BF; dacă se foloseşte numai start atunci se listează toate faptele având contorul

11
identificatorului mai mare sau egal cu start; dacă se foloseşte şi start şi end atunci se afişează
toate faptele al căror contor satisface relaţia:

start ≤ i ≤ end (**)

Dacă apare şi maxim împreună cu start şi end, atunci vor fi listate cel mult maxim fapte
(primele), care respectă condiţia (**).
Altă posibilitate mai simplă de a vedea BF este să se deschidă fereastra pentru fapte
(meniul Window, opţiunea Facts), în care se va afişa continuu starea BF.

Exerciţii
1. Introduceţi de la nivel superficial mai multe fapte în BF.
2. Urmăriţi faptele introduse prin comanda “facts”, folosită cu un număr diferit de
argumente, cât şi prin deschiderea ferestrei de fapte.

b) Introducerea faptelor prin comanda “deffacts”


Comanda “deffacts” este o comandă de definire a unei construcţii. În CLIPS există
posibilitatea ca utilizatorul să creeze construcţii (reguli, obiecte, grupuri de fapte, variabile
globale, ş.a.) prin folosirea unor comenzi corespunzătoare. Toate comenzile de definire de
construcţii au o formă sintactică apropiată şi vor putea fi folosite la nivel superficial, sau vor
putea fi înscrise într-un fişier, cu ajutorul unui editor de texte avut la dispoziţie şi apoi aduse sub
interpretorul CLIPS, printr-o comandă adecvată, de încărcare a fişierului respectiv.
Comanda “deffacts” este aceea care permite utilizatorului să definească un grup de fapte.
Sintaxa acestei comenzi este:

(deffacts <nume-deffacts> [<comentariu-opţional>] <<fapt>>)

nume-deffacts - trebuie să fie un simbol şi va fi un identificator al grupului respectiv de


fapte;
comentariu-opţional - trebuie să fie un şir de caractere şi va fi folosit pentru a documenta
programul, în sensul că utilizatorul poate nota explicaţii care să ajute înţelegerea programului.
Mai menţionăm că toate comenzile CLIPS pot fi înscrise pe oricâte linii, cu respectarea
condiţiei de închidere corectă a tuturor parantezelor.
Exemplu de utilizare:

(deffacts stare-initiala “aceasta este starea initiala a problemei”


(temperatura intrare-cuptor 1050)
(presiune fluid-lucru 240)
(stare contactor1 buna)
)

În legătură cu comanda “deffacts” mai trebuie făcute următoarele observaţii. Faptele


înscrise într-o comandă “deffacts” sunt introduse în BF numai după o comandă (reset); asupra
altor efecte ale acestei comenzi vom reveni. Comenzile “deffacts” pot fi plasate şi în fişiere,
create cu ajutorul editoarelor de texte; în mod obişnuit, fişierele respective trebuie să aibă
extensia: .clp (comenzi "assert" nu pot fi puse într-un fişier din care vrem să încărcăm fapte).
Pentru a încărca sub interpretorul CLIPS conţinutul unui asemenea fişier se va folosi comanda:

(load <nume-fişier>)

unde argumentul respectiv trebuie să fie de tip şir de caractere sau simbol. El va preciza numele
fişierului pe care îl dorim a fi încărcat; în funcţie de plasarea acestui fişier într-un anumit

12
director, va fi necesar eventual să precizăm şi calea, ţinând seama de regulile de scriere a
şirurilor de caractere. De exemplu, dacă “deffacts-ul” de mai sus se găşeste înscris în fişierul
fisier1.clp, atunci, de sub interpretorul CLIPS, putem încărca acest fişier prin comanda (am
presupus că a fost necesară precizarea căii):

(load “d:\\work\\fisier1.clp”)

Mai simplu, putem folosi direct comanda Load, din meniul File. Mai menţionăm că într-un fişier
pot fi înscrise oricâte comenzi de definire de construcţii, după cum putem păstra construcţii în
mai multe fişiere, pe care să le încărcăm atunci când este necesar. Un efect invers faţă de “load”
are comanda:

(save <nume-fisier>)

unde argumentul trebuie să fie de tip şir de caractere sau simbol. Efectul este de salvare în
fişierul respectiv a tuturor definiţiilor de construcţii care au fost introduse în sistem până la acel
moment.

Faptele din BF pot fi şterse, atunci când ele nu mai sunt adevărate sau necesare în BF, cu
ajutorul comenzii:

(retract <<<contor>>>)

unde contor trebuie să fie un întreg pozitiv, reprezentând contorul din identificatorul faptului
care trebuie şters. Exemple de utilizare a acestei comenzi sunt:

(retract 1) (retract 5 23 25)

Observaţii
1. Dacă se cere ştergerea unui fapt care nu există (locul respectiv este deja gol), se
afişează eroare.
2. Există şi următoarea formă a comenzii “retract”: (retract *), care determină ştergerea
tuturor faptelor din BF.

Exerciţii
1. Răspundeţi la următoarele întrebări:
• Care sunt deosebirile dintre comenzile "deffacts" şi "assert" ?
• Câte fapte se pot afla într-o BF, după rularea unui program, între identificatorii f-10 şi f-15 ?
• Cum poate utilizatorul vedea primele 25 fapte aflate între identificatorii f-15 şi f-80 ?
2. Explicaţi rezultatul următoarei comenzi, dată la nivel superficial:
(assert (aaa1) (bbb2) ) (assert (ccc3) ) ¶
3. Concepeţi tiparele de fapte pentru următoarele cazuri şi introduceţi în BF câteva exemple de
fapte care să respecte tiparele stabilite (introducerea faptelor să se facă pentru unele de la nivel
superficial, pentru altele dintr-un fişier creat cu un editor de texte).
a) reprezentarea sub formă de fapte a mulţimilor;
b) reprezentarea sub formă de fapte a relaţiilor de rudenie;
c) reprezentarea prin fapte a diferitelor figuri geometrice plane;
d) reprezentarea sub formă de fapte a universului de discurs pentru o celulă flexibilă de
fabricaţie (se numeşte univers de discurs o porţiune din mediul înconjurător pentru
care avem sau vrem să realizăm un model de reprezentare a cunoaşterii; un model de
reprezentare a cunoaşterii este un model al realităţii, care poate fi introdus în memoria
unui calculator, în scopul efectuării de raţionamente).

13
LUCRAREA NR. 2

Definirea regulilor în CLIPS


1. Comanda de construire a unei reguli

Pentru a introduce o regulă în baza de reguli (BR) se poate folosi comanda “defrule”,
care fiind de tip definire de construcţie are o formă similară cu comanda “deffacts”. Sintaxa
pentru “defrule” este:

(defrule <nume-regulă> [<comentariu-opţional>] [(declare (salience <întreg>) )]


<<tipar>> => <<acţiune>> )

nume-regulă - trebuie să fie un simbol şi va fi identificatorul regulii respective;


comentariu-opţional - trebuie să fie un şir de caractere şi va fi folosit pentru a documenta
programul; aici se vor înscrie eventualele explicaţii necesare în legătură cu regula;
partea stângă a regulii poate conţine o declaraţie privind prioritatea ataşată regulii
(declaraţie de tip ”salience”); argumentul care apare trebuie să fie un număr întreg cuprins
între -10000 şi 10000 şi stabileşte prioritatea ataşată regulii; declaraţia respectivă poate lipsi din
definiţia regulii, caz în care regula va primi implicit prioritatea zero, adică o prioritate medie.
tipar - este un element al părţii de condiţie a regulii şi are forma unei liste cu una sau mai
multe poziţii (deci conform definiţiei pentru o listă în CLIPS, un tipar este delimitat de
paranteze);
acţiune - este o comandă CLIPS, care va fi de tip funcţie şi va fi executată atunci când
regula este executată; totalitatea acţiunilor unei reguli constituie partea dreaptă a regulii; aşa cum
apare în sintaxa dată mai sus, partea de condiţie este separată de cea de acţiune prin succesiunea:
=>

Pentru fiecare regulă trebuie folosită o comandă “defrule”; dacă o comandă “defrule” se
foloseşte succesiv cu acelaşi nume de regulă, atunci ultima definiţie o înlocuieşte pe cea
anterioară. Regulile se introduc în BR prin folosirea comenzilor “defrule” la nivel superficial,
sau prin înscrierea acestor comenzi în fişiere şi încărcarea prin “load”.
O regulă din BR se poate găsi în una din stările: inactivată, activată şi în execuţie.
O regulă este activată dacă toate tiparele ei pot fi potrivite pe faptele din BF din
momentul respectiv; în caz contrar regula este inactivată. Aceasta înseamnă că partea de condiţie
a regulilor este în CLIPS sub formă de condiţie AND. De exemplu, fiind dată regula R introdusă
prin definiţia:

(defrule R “acesta este¶


un exempluӦ
(a 1)¶
(b 2)¶
=>¶
(assert (c 3) )

ea va fi activată dacă în BF se găsesc faptele (a 1) şi (b 2).

O regulă activată este una pregătită pentru execuţie; la un moment dat, în timpul lucrului
SE, mai multe reguli pot fi activate. Astfel, se defineşte agenda ca fiind mulţimea tuturor
regulilor activate la un moment dat.

14
Observaţii
1. O regulă poate să aibă şi zero tipare; felul în care este activată o asemenea regulă va fi
explicat ulterior.
2. Din punct de vedere al sintaxei este permis ca o regulă să nu aibă nicio acţiune, deşi
aşa ceva nu este util.
3. Ca un caz particular, agenda poate conţine şi zero reguli, atunci când nicio regulă din
BR nu este satisfăcută de faptele prezente în BF.

Execuţia unei reguli înseamnă execuţia, în ordinea în care apar, a acţiunilor din partea
dreaptă a regulii. Acţiunile sunt reprezentate de comenzi CLIPS de tip funcţie. În exemplul de
mai sus, partea de acţiune a regulii R conţine o singură comandă “assert”, astfel că după execuţia
regulii vom constata că în BF a fost introdus un nou fapt: (c 3). Execuţia regulilor în CLIPS nu
se face în mod automat, ci numai dacă utilizatorul transmite comanda:

(run [<limita>])

Argumentul respectiv trebuie să fie un număr întreg mai mare sau egal cu -1 şi el dă
numărul maxim de reguli care să fie executate. Dacă argumentul “limita” nu este prevăzut sau
are valoarea -1, atunci execuţia regulilor va continua până când nu mai există nicio regulă în
agendă (după ce o regulă este executată ea este scoasă din agendă); dacă argumentul este
prevăzut, atunci execuţia va înceta după cel mult un număr de execuţii de reguli egal cu “limita”.
Comanda ”run” se poate da şi din meniul Execution.
Din cele prezentate până aici rezultă că apare o interdependenţă între reguli şi BF:
regulile sunt activate sau nu, în funcţie de faptele prezente în momentul respectiv în BF. Această
interdependenţă este sub controlul motorului de inferenţe.

2. Ciclul de lucru al motorului de inferenţe în CLIPS

În CLIPS motorul de inferenţe lucrează ciclic, conform paşilor precizaţi în continuare


(este de menţionat că acest ciclu de lucru are un caracter de generalitate pentru felul în care
lucrează SE folosind mecanismul de căutare înainte):

Pas 1. Determină regulile care au partea de condiţie satisfăcută, ţinând seama de starea
BF din momentul respectiv, adică determină agenda.
Pas 2. Ordonează regulile din agendă şi selectează regula aflată pe prima poziţie în
agendă.
Pas 3. Execută acţiunea/acţiunile din partea dreaptă a regulii selectate; apoi se revine la
pasul 1 (oprirea se produce în conformitate cu explicaţiile următoare).

În legătură cu acest ciclu de lucru se pot face mai multe observaţii.


1. Dacă paşii 1 şi 2 se execută de îndată ce există BR şi BF, fără nicio altă comandă
suplimentară, pasul 3 intră în funcţiune numai dacă utilizatorul transmite comanda “run”. După
ce a fost dată această comandă, ciclul de mai sus se execută în mod repetat, oprirea putând să
apară în conformitate cu explicaţiile date la comanda “run”.
2. Cei trei paşi de mai sus au denumiri în funcţie de scopul lor. Pasul 1 se numeşte de
potrivire, fiind vorba de găsirea regulilor ale căror tipare se potrivesc pe faptele din BF. Procesul
de potrivire are o serie de aspecte specifice, ce vor fi discutate separat. Pasul 2 se numeşte de
rezolvare a conflictului; denumirea provine din aceea că mulţimea regulilor satisfăcute la un
moment dat (agenda) se mai numeşte şi mulţime conflictuală, deoarece regulile din agendă sunt
în conflict, în ceea ce priveşte obţinerea execuţiei. Rezolvarea conflictului se face în funcţie de
două criterii; primul este cel dat de prioritatea ataşată regulii. Din acest punct de vedere există în

15
CLIPS şi posibilitatea de a aloca regulilor o prioritate de tip dinamic (Meniul Execution,
Options, Salience Evaluation), caz în care pasul de rezolvare a conflictului este mai complicat;
acest aspect nu este prezentat în acest referat. Cel de-al doilea criteriu de rezolvare a
conflictului se referă la o anumită strategie pe care o foloseşte motorul de inferenţe pentru a
ordona regulile în agendă; fără a intra în detalii, există şapte asemenea strategii, care pot fi fixate
din meniul Execution, opţiunea Options, Strategy. Pasul 3 se numeşte de execuţie.
3. Prin repetarea ciclului de mai sus se produce o evoluţie a BF: după fiecare execuţie a
unei reguli BF se poate modifica, deoarece în majoritatea cazurilor acţiunile regulilor sunt şi de
tipul celor care afectează BF (de exemplu, “assert”, “retract”). După efectuarea pasului 3, BF
putând fi modificată, pasul 1 are consistenţă, în sensul că noi reguli pot fi satisfăcute, în timp ce
altele nu mai sunt satisfăcute.

3. Urmărirea agendei; proprietatea de refracţie

În timpul evoluţiei unui SE apare frecvent ca necesară urmărirea agendei şi în acest sens
există în CLIPS comanda:

(agenda)

Rezultatul va fi afişarea conţinutului agendei, adică a regulilor aflate în agendă, în forma:

nr nume-regulă: f - i1, f - i2, ...f - ij, ... , f - in

în care nr indică prioritatea ce a fost atribuită regulii respective, iar f - ij sunt identificatorii
faptelor care au satisfăcut tiparele regulii (aceştia apar în ordinea tiparelor din regulă). Afişarea
agendei se poate obţine şi activând fereastra corespunzătoare din meniul Window.

Exerciţiu
Scrieţi regula care să fie activată dacă în BF se găsesc faptele (a), (b) şi (c) (activarea să
se producă numai dacă sunt prezente toate cele trei fapte în BF) şi care să introducă în BF, la
execuţie, faptul (d) . Urmăriţi BF şi agenda. Efectuaţi rularea.

CLIPS este dotat cu o caracteristică importantă pentru SE numită refracţie. Se consideră


că această proprietate a fost copiată de la celula nervoasă (după ce un neuron a transmis un
impuls ca urmare a unui stimul, acelaşi stimul nu-l va mai putea excita din nou, pentru o anumită
perioadă de timp). În cazul unui SE refracţia înseamnă că o regulă nu va fi activată de un acelaşi
fapt sau acelaşi grup de fapte în mod repetat. Fără această proprietate un SE ar fi blocat într-o
buclă infinită, chestiune ce se poate ilustra chiar cu regula din exerciţiul precedent, sau cu regula
R dată anterior.

Exerciţiu
Dacă aşa ceva este necesar, cum poate fi depăşită refracţia? Exemplificaţi pe cazul din
exerciţiul anterior (pentru rezolvare urmăriţi şi prima din observaţiile următoare).

Observaţii
1. Ceea ce reţine motorul de inferenţe din punct de vedere de al refracţiei sunt faptele
care au determinat activarea, împreună cu identificatorii acestora din BF. Aceeaşi combinaţie de
identificatori nu va putea face o nouă activare, în schimb în combinaţia respectivă ordinea
contează, astfel că aceleaşi fapte pot determina mai multe activări, considerate în ordini diferite,
evident cu condiţia asigurării potrivirilor necesare. De exemplu, aşa cum vom vedea ulterior,
agenda ar putea arăta la un moment dat de forma:

16
0 R1 f - 1, f - 2
0 R1 f - 2, f - 1

aceasta însemnând că regula R1 este activată de două ori de aceleaşi fapte, cu identificatorii f - 1
şi f - 2. În această situaţie, la comanda (run) regula R1 va fi în principiu executată de două ori,
conform celor două plasări ale ei în agendă.

Exerciţiu
1. Care este condiţia pe care trebuie să o îndeplinească faptele din explicaţia de mai sus,
din punct de vedere al procesului de potrivire?
2. Ţinând seama de răspunsul la întrebarea 1, rezultă că mai pot fi posibile şi alte plasări
ale lui R1 în agendă, tot datorită faptelor f - 1 şi f - 2. Care sunt acestea?
3. În ce condiţii se poate întâmpla ca după prima execuţie a regulii R1 din explicaţia de
mai sus, următoarea sau următoarele execuţii să nu se mai producă?

2. Dacă la un moment dat este necesară depăşirea refracţiei, în legătură cu o anumită


regulă, o posibilitate este şi folosirea comenzii:

(refresh <nume-regulă>)

efectul fiind de replasare în agendă a regulii precizate prin argumentul respectiv, în condiţiile în
care potrivirea pentru acea regulă este satisfăcută, iar neactivarea ei se datorează refracţiei.

Exerciţiu
Verificaţi efectul comenzii "refresh".

4. Iniţializarea în CLIPS

Comanda “reset” este cea care poate fi folosită în CLIPS pentru a obţine un efect de
iniţializare. Ea are forma:

(reset)

şi are următoarele efecte:

• videază BF;
• elimină toate regulile din agendă;
• introduce în BF toate faptele din toate comenzile “deffacts” existente în sistem.

Evident, conform ultimului efect de mai sus, după o comandă ”reset” agenda va fi
recalculată. Comanda “reset” poate fi activată şi din opţiunea corespunzătoare din meniul
Execution.
Datorită efectelor sale, comanda “reset” este cea care se foloseşte de obicei pentru a lansa
în lucru un SE/un program scris în CLIPS. În acest sens mai există un efect, obţinut tot în urma
execuţiei acestei comenzi şi care poate fi util pentru rezolvarea “pornirii” unui SE. Astfel,
interpretorul CLIPS introduce în mod automat următoarea comandă:

(deffacts initial-fact (initial-fact) )

adică introduce de la sine o comandă “deffacts” în interiorul căreia se află un singur fapt, cu un
singur câmp: initial-fact. Rezultă că după efectuarea comenzii “reset”, vom găsi în BF, în mod
automat, faptul (initial-fact), care va fi plasat la identificatorul f – 0 (evident, conform celor

17
spuse anterior, vor exista în BF după ”reset” şi faptele din toate comenzile ”deffacts” introduse
de programator). Astfel, chiar dacă utilizatorul nu a introdus în sistem nicio comandă “deffacts”,
după “reset” BF va conţine un fapt; este de menţionat că (initial-fact) apare în BF şi după
comanda ”clear”, cea care va prezentată în continuare. Rolul lui “initial-fact” este de crea o
posibilitate de începere a unui program scris în CLIPS. În acest sens, este de observat că rularea
în CLIPS nu se produce dacă nu există reguli plasate în agendă, iar dacă BF nu conţine fapte,
regulile având tipare în partea de condiţie nu pot fi activate. Totuşi, dacă în BR există reguli cu
partea de condiţie vidă (zero tipare), motorul de inferenţe le consideră activate indiferent de
faptele existente în BF, adică asemenea reguli vor fi activate şi dacă în BF nu există niciun fapt.
De asemenea, programatorul poate concepe reguli care să aibă în partea de condiţie doar tiparul
(initial-fact), iar aceste reguli vor fi activate de faptul implicit ce va exista în BF la
identificatorul f – 0. Regulile de pornire pentru un SE vor avea o parte de acţiune
corespunzătoare, încât ele să efectueze acţiunile de startare pentru SE (de exemplu, primirea
datelor iniţiale de la utilizator).

Exerciţiu
1. Scrieţi o regulă de pornire şi observaţi comportarea ei după comanda “reset”.

5. Comenzi utilitare pentru reguli şi fapte

În afară de comenzile deja descrise (“facts”, “agenda”) există şi alte comenzi care pot
ajuta utilizatorul în construirea şi urmărirea BF şi a BR.
Se poate afla conţinutul BR prin comanda:

(rules)

care va avea ca efect afişarea numelor tuturor regulilor prezente în BR. Putem vedea întreg
conţinutul unei reguli prin comanda:

(ppdefrule <nume-regulă>)

singurul argument specificând numele regulii pe care o vrem afişată integral (denumirea este o
prescurtare de la “pretty print defrule”; în CLIPS există mai multe comenzi a căror denumire
începe cu "pp" şi care au o acţiune similară - de tipărire a conţinutului unei constucţii). Efectele
comenzilor “rules” şi “ppdefrule” (ca şi cel al comenzii “refresh”) se pot obţine şi folosind
meniul Browse, opţiunea Defrule Manager.
Comenzi similare există şi pentru “deffacts”. Astfel putem vedea numele tuturor
comenzilor “deffacts” existente prin:

(list-deffacts)

iar conţinutul unui “deffacts” prin:

(ppdeffacts <nume-deffacts>)

şi aceste comenzi fiind accesibile şi din meniul Browse, opţiunea Deffacts Manager.
Comenzile “defrule” şi “deffacts” pot fi şterse prin:

(undefrule <nume-regulă>) şi respectiv (undeffacts <nume-deffacts>)

efectul fiind acela de ştergere a regulii specificate, respectiv a comenzii “deffacts” cu numele
precizat prin argumentul respectiv (ştergerea se poate face şi din meniul Browse). Este de

18
observat că în cazul ştergerii unei comenzi “deffacts” nu se produce ştergerea faptelor din BF, ci
ştergerea definiţiei construcţiei “deffacts” respective. De asemenea, este de observat că
utilizatorul, intrat sub interpretorul CLIPS, poate şterge şi construcţia “deffacts” introdusă
automat de sistem (cea cu numele “initial-fact”); evident utilizatorul va face aşa ceva dacă vrea
să elimine efectul pe care îl are comanda respectivă, adică introducerea lui (initial-fact) în BF
după ”reset”. O comandă de ştergere generală este comanda:

(clear)

care şterge toate informaţiile din sistem: se videază BF, BR, agenda şi sunt şterse toate
comenzile de definire de construcţii (în particular toate comenzile “deffacts”, dar nu şi cea
implicită privind faptul (initial-fact)), adică sistemul devine ca în momentul pornirii (comanda
“clear” este disponibilă şi din meniul Execution).
Evident, definiţiile de construcţii şterse prin comanda “clear” vor putea fi reîncărcate din
fişierele unde sunt păstrate. Astfel, reamintim că definiţiile de reguli, ca şi comenzile “deffacts”,
fiind comenzi de definire de construcţii, pot fi înscrise în fişiere şi apoi încărcate prin comanda
“load”; de asemenea, acestea pot fi salvate în fişiere prin comanda “save” (de observat că o
comandă “save” are un efect de salvare a întregii BR existente în acel moment, ca şi salvarea
tuturor celorlalte comenzi de definire de construcţii, într-un singur fişier; dacă se doreşte o
salvare preferenţială, de exemplu numai pentru anumite reguli, atunci trebuie conceput un
program corespunzător).

6. Comanda de afişare

Una din comenzile care este frecvent folosită în partea de acţiune a regulilor este:

(printout <nume-logic> <<element-de-ieşire>>)

unde <nume-logic> indică destinaţia ieşirii; pentru că iniţial vom folosi această comandă numai
pentru afişarea pe terminal, argumentul nume-logic va fi t (ieşirea se poate face şi la alte canale
de ieşire sau într-un fişier, aşa cum vom arăta ulterior); rezultatul acestei comenzi va fi înscrierea
elementelor de ieşire; acestea pot fi în principiu orice comenzi CLIPS, cu excepţia definiţiilor de
construcţie. Dacă drept element de ieşire se foloseşte apelul unei funcţii sau numele unei
variabile globale, atunci se afişează rezultatul evaluării elementului respectiv. Cele mai folosite
elemente de ieşire în comanda “printout” vor fi constantele, de exemplu pentru afişarea unor
mesaje. În acest sens sunt de reţinut următoarele:

• o constantă de tip şir de caractere este afişată fără ghilimelele de început şi de sfârşit;
• între mai multe elemente afişate nu se introduce în mod automat niciun spaţiu; de aceea
programatorul trebuie să aibă grijă de obţinerea spaţierii, de exemplu prin introducerea unui
element de ieşire de forma “ ” (un spaţiu plasat între ghilimele);
• după execuţia comenzii “printout” prompterul nu trece la o line nouă în mod automat şi de
aceea, aproape întotdeauna, ultimul element de ieşire într-o comandă printout va fi constanta
crlf, care va avea drept efect trecerea prompterului pe o linie nouă.

Exerciţii
1. Verificaţi efectul următoarelor comenzi:
• (printout t (+ 12 13))
• (printout t (+ 12 13) crlf)
• (printout t CLIPS e bun crlf)
• (printout t “CLIPS e bun” 23 “ ” OK (* 2 2) crlf)
• (prinout t (assert (a) ) crlf)

19
• (printout t (facts) crlf)
2. Scrieţi o regulă care să afişeze un mesaj de pornire a funcţionării unui SE atunci când
în BF se găseşte un anumit fapt de începere a lucrului.

7. Documentarea programelor în CLIPS; comenzi pentru depanare în CLIPS

O primă posibilitate de documentare în ceea ce priveşte regulile şi faptele (numai pentru


cele introduse prin “deffacts”) este aceea de a folosi parametrul opţional (cel sub formă de şir de
caractere) ce apare în comenzile de definire de construcţii respective. O a doua posibilitate este
aceea de a folosi caracterul rezervat ;. Astfel un comentariu începe cu ; şi se încheie cu ¶. Tot ce
apare după ; nu este luat în considerare de interpretor. Există o deosebire între comentariul scris
între ghilimele într-o definiţie de construcţie şi cel care începe cu ; . Aceasta se referă la
comportarea în raport cu comenzile de tip “pretty print” (“ppdefrule”, “ppdefacts”): la afişarea
conţinutului unei construcţii cu o asemenea comandă, comentariul scris între ghilimele apare, în
timp ce acela prevăzut după ; nu.

Exerciţiu
Verificaţi explicaţia de mai sus pe un exemplu, obţinut prin următoarele comenzi (veţi
respecta întocmai scrierea din referat, pe rânduri):

(defrule R1 “Acesta e exemplul meu” => ; el merge grozav (assert (a)) ¶


(assert (b)) ) ¶
(ppdefrule R1) ¶
(reset) ¶
(run) ¶

Concluzia după acest exemplu este aceea că în programele CLIPS vom folosi pentru
documentare mai ales comentariul inclus între ghilimele, deoarece acela introdus prin ; se pierde.

Exerciţiu
Când poate fi util un comentariu care este introdus prin ; ?

În ceea ce priveşte depanarea, o posibilitate este de a urmări BF şi agenda prin comenzile


“facts”, “agenda” sau prin deschiderea ferestrelor respective.
O comandă utilă în depanarea programelor este:

(watch {facts, rules, activations, compilations, statistics, all})

Mai sunt şi alţi câţiva parametri care pot interveni în comanda “watch”, dar care nu se
referă la BF şi BR (urmărirea variabilelor globale, a obiectelor, a funcţiilor). Comanda “watch”
are drept efect urmărirea anumitor elemente ale programului CLIPS în timpul lucrului cu
interpretorul. Dacă parametrul folosit în comandă este “all” atunci vor fi urmărite toate
elementele. Efectul de dezactivare se poate obţine prin comanda:

(unwatch {facts, rules, activations, compilations, statistics, all})

în care dacă parametrul folosit este “all” dezactivarea se face pentru toate elementele, iar dacă se
foloseşte un alt parametru (de exemplu, (unwatch facts) ), atunci dezactivarea se face pentru
elementul respectiv. De observat că, atât la activarea urmăririi cât şi la dezactivarea acesteia,
dacă ne interesează efectul asupra a două elemente, atunci comanda va trebui transmisă de două
ori, cu folosirea parametrilor corespunzători. De altfel, comanda ”watch” cu parametrii ei poate
fi selectată mai uşor din meniul Execution.

20
Dacă se urmăresc faptele (s-a dat comanda (watch facts)), atunci utilizatorul va fi
informat, printr-un mesaj, ori de câte ori un fapt este introdus sau scos din BF. Dacă se urmăresc
activările (comanda (watch activations) ), atunci se va afişa un mesaj ori de câte ori o regulă
este introdusă sau scoasă din agendă. Dacă se urmăresc regulile (comanda (watch rules) ),
atunci se afişează un mesaj de fiecare dată când o regulă este executată. Dacă se urmăresc
compilările (comanda (watch compilations) ), atunci se afişează mesaje la încărcarea unor
definiţii de construcţii dintr-un fişier (interpretorul face un fel de compilare la încărcarea
definiţiilor de construcţii). Dacă se urmăresc statisticile (comanda (watch statistics) ), atunci
se afişează mesaje, după rulare, cu privire la numărul de fapte din BF, numărul de reguli din
agendă, numărul de reguli executate şi timpul de rulare.
O altă comandă de depanare este:

(matches <nume-regulă> )

Ea este utilă atunci când în BR există reguli cu multe tipare şi dorim să ştim care tipare au fost
satisfăcute şi care nu la un moment dat. Argumentul va fi numele regulii care vrem să fie
urmărită din punct de vedere al potrivirilor. Comanda “matches” este accesibilă şi din meniul
Browse, opţiunea Defrule Manager. Pentru a ilustra efectul acestei comenzi se va folosi
următorul exemplu:

(defrule Aa (a) (b) (c) => )¶


(assert (b) )¶
(matches Aa)¶
(assert (a) )¶
(matches Aa)¶
(assert (c) )¶
(matches Aa)¶

Mesajele afişate se referă la felul în care se potrivesc tiparele regulii urmărite, în ordinea în care
apar ele în definiţia regulii, cât şi la felul în care sunt potrivite grupurile de tipare luate împreună:
primele două tipare luate împreună, primele trei tipare luate împreună, ş.a.m.d. Această urmărire
a grupurilor de tipare devine importantă atunci când în tiparele regulii se folosesc şi variabile.
Ultimul mesaj afişat la comanda “matches” se referă la activările regulii (plasarea sau nu, a
acesteia în agendă).
Tot pentru depanare este utilă şi comanda:

(set-break <nume-regulă> )

Efectul este de stabilire a unui punct de oprire (“break-point”) la regula cu numele dat prin
argument. În acest caz rularea se va opri înainte de execuţia regulii respective, cu observaţia că
este necesar ca măcar o regulă să fie executată (prin comanda “run”), înainte ca un punct de
oprire să poată opri execuţia. De altfel, în acest fel, un punct de oprire poate fi depăşit prin
repetarea comenzii de rulare. După fiecare oprire, pentru depanare, utilizatorul poate cerceta BF
şi agenda.
Putem lista toate punctele de oprire prin comanda:

(show-breaks)

şi putem îndepărta un punct de oprire, sau pe toate, prin comanda:

(remove-break [ <nume-regulă> ] )

21
Dacă argumentul există atunci se va îndepărta numai punctul de oprire corespunzător regulii
indicate, iar dacă argumentul lipseşte sunt eliminate toate punctele de oprire. Mai menţionăm că
punctele de oprire pot fi ataşate regulilor şi din meniul Browse, opţiunea Defrule Manager.
Pentru a ilustra efectul punctelor de oprire efectuaţi următorul experiment:

1. Încărcaţi dintr-un fişier, sau de la nivel superficial următoarele trei reguli:

(defrule unu => (assert (doi) ) )


(defrule doi (doi) => (assert (trei) ) )
(defrule trei (trei) => )

2. Transmiteţi comenzile:

(set-break unu)¶
(set-break doi)¶
(set-break trei)¶
(run)¶
(run)¶
(run)¶

3. Explicaţi rezultatele obţinute.

După cum s-a mai spus, un efect de oprire după rularea unui anumit număr de reguli se
poate obţine şi prin comanda “run”, dată cu un argument corespunzător; în plus, în meniul
Execution există şi opţiunea Step care are un efect de rulare pas cu pas (este echivalentă lui (run
1), cu observaţia că pasul poate fi şi schimbat la o valoare mai mare ca 1, în opţiunea
Preferences, din acelaşi meniu Execution).

Exerciţii
1. Răspundeţi la următoarele întrebări:
• Cum pot fi introduse regulile în BR?
• Care este deosebirea între BR şi agendă?
• Care este ciclul de lucru al motorului de inferenţe în CLIPS?
• După comanda (run 10) câte execuţii de reguli s-ar putea produce?
• Care sunt situaţiile în care se opreşte rularea în CLIPS?
• Ce înseamnă refracţia în domeniul programării bazate pe reguli?
• Când este activată o regulă care are zero tipare?
• Câte reguli se execută simultan în CLIPS?
• Ce posibilităţi sunt pentru documentarea programelor în CLIPS? Explicaţi diferenţa
între ele.
• Ce posibilităţi pentru depanare oferă CLIPS?

2. Verificaţi funcţionarea programului CLIPS format din următoarea regulă:


(defrule r (a) => (printout t “Ura!” crlf) (refresh r) )
Experimentul se va face creând condiţiile ca regula să fie activată şi efecuând rularea cu
comanda (run 25).

3. Scrieţi programul CLIPS (aceasta înseamnă BF şi BR) care să materializeze


următoarea funcţie binară:

F ( x , y ,z )= x⋅ y⋅ zUx⋅ y⋅ z

22
4. Scrieţi programul CLIPS care să simuleze funcţionarea automatului secvenţial
specificat prin următorul tabel de tranziţie:

x S0 S1 S2 S3
0 S1 S1 S3 S3
1 S2 S0 S2 S0

unde am notat cu x variabila de intrare şi cu Si stările automatului.

5. Agenţii software pot fi clasificaţi în următoarele categorii, în funcţie de proprietăţile


pe care le îndeplinesc:

• Agenţi reactivi sunt cei care reacţionează la schimbările din mediu.


• Agenţi planificatori sunt cei care sunt capabili să determine un plan de rezolvare a unui
scop primit.
• Agenţi de tip hibrid sunt cei care reacţionează atât la schimbările din mediu, cât şi la
scopurile primite, fiind capabili să rezolve ambele tipuri de situaţii.

a) Concepeţi faptele şi regulile CLIPS care să poată determina pentru un agent tipul său, în
funcţie de informaţiile care există despre acesta în BF.

b) Un agent de tip reactiv este indicat pentru a rezolva probleme de monitorizare, un agent
planificator este indicat pentru a rezolva probleme de fabricaţie, iar un agent hibrid poate rezolva
ambele tipuri de probleme. Scrieţi regulile CLIPS care preluând rezultatul de la punctul anterior
să precizeze care sunt tipurile de probleme ce pot fi rezolvate conform tipului agentului pentru
care există informaţii în BF.

23
LUCRAREA NR. 3

Procesul de potrivire în CLIPS; variabilele în CLIPS


1. Variabile locale simple; mecanismul de potrivire în cazul folosirii variabilelor

Precum în general în programare, variabilele sunt folosite în programarea bazată pe reguli


pentru a păstra valori; în plus, aici ele vor juca un rol important în procesul de potrivire.
Variabilele în CLIPS se pot clasifica după mai multe criterii. Astfel, după scopul lor (modul în
care sunt vizibile şi produc efecte) variabilele sunt locale şi globale. După numărul de câmpuri
care pot fi păstrate/preluate de o variabilă acestea sunt simple şi multiple. După felul în care au
un nume de identificare şi păstrează efectiv valori sau nu au un nume şi sunt doar folosite pentru
procesul de potrivire, variabilele pot fi specificate şi nespecificate.
O variabilă CLIPS locală, simplă, specificată este reprezentată sub forma:

?<nume-variabilă>

unde nume-variabilă trebuie să fie un simbol CLIPS care să înceapă cu o literă şi între ? şi
numele variabilei nu trebuie să existe niciun spaţiu.
Exemple de variabile CLIPS sunt: ?x, ?tensiunea_in_A, ?A21, ?curent-grila.
Înainte ca o variabilă să poată fi folosită trebuie să fi primit o valoare; în programarea
bazată pe reguli se spune că variabila a fost legată. De exemplu, la introducerea următoarei
definiţii, interpretorul va semnala eroare:

(defrule regula-gresita => (printout t ?x crlf) )

Eroarea este aceea că a fost folosită variabila ?x în partea de acţiune a regulii, fără ca aceasta să
fi fost legată în prealabil (fără să-i fi atribuit o valoare). Variabilele locale pot fi folosite în partea
dreaptă a unei reguli numai după ce au primit o valoare în partea stângă a regulii respective.
Astfel, un exemplu de folosire corectă a unei variabile într-o regulă este următorul:

(defrule R1 (este-student ?nume) => (assert (a terminat liceul ?nume) ) )

Dacă în BF este introdus faptul (este-student Ion) regula R1 va fi activată şi dacă se dă şi


comanda “run” vom constata că în BF este introdus un nou fapt: (a terminat liceul Ion).
Explicaţia este aceea că o variabilă folosită în partea de condiţie a unei reguli se va lega în
procesul de potrivire la o anumită valoare, în conformitate cu faptele existente în BF, şi apoi
variabila poate fi folosită în partea de acţiune a regulii, ea păstrând valoarea la care a fost legată.

Sunt de reţinut următoarele observaţii în legătură cu folosirea variabilelor.


1. O variabilă locală, simplă prezentă într-un tipar în partea de condiţie a unei reguli va
putea fi potrivită de un singur câmp, indiferent de valoarea acestuia (o variabilă simplă nu se
poate lega la două câmpuri). De exemplu, în regula R1 de mai sus, un fapt de forma (este-student
Ion Ionescu) nu va determina activarea regulii, deoarece variabila simplă ?nume nu se poate
lega la două câmpuri. În schimb, faptul (este-student -234.45) va determina activarea regulii,
deoarece variabila ?nume se leagă la un câmp fără a introduce restricţii asupra tipului acestuia
(vom arăta ulterior cum se pot introduce asemenea restricţii).
2. O variabilă locală rămâne legată numai pe durata de acţiune a regulii în care apare; în
acest sens nu va fi creată nicio legătură între două reguli care folosesc aceeaşi variabilă. Astfel,
fie următorul exemplu:

24
(defrule R2 (fapt ?x) => (assert (a ?x) ) )
(defrule R3 (fapt ?x 12) => (assert (b ?x) ) )

Deşi în cele două reguli s-a folosit aceeaşi variabilă ?x, între regulile respective nu va exista
nicio legătură, pentru că o variabilă rămâne legată numai pe durata de acţiune a regulii.
3. O variabilă poate fi folosită de mai multe ori în partea de acţiune a unei reguli
(respectând condiţia de a fi deja legată) şi poate fi folosită şi în operaţii diferite, ca în exemplul
următor:

(defrule R4 (este-in-stare-de-functiune calculatorul ?nume-calculator) =>


(assert (poate fi utilizat ?nume-calculator) )
(printout t “Gata de lucru “ ?nume-calculator crlf ) )

4. O aceeaşi variabilă poate fi utilizată în mod repetat în partea de condiţie a unei reguli,
pentru a verifica anumite condiţii prin procesul de potrivire, ca în exemplul următor:

(defrule R5 ”determinare-autor-curs - se determina daca un cadru didactic


are curs tiparit”
(cadrul-didactic ?nume tine disciplina ?obiect)
(manualul ?obiect aparut in anul ?an are ca autor pe ?nume)
=>
(printout t “cadrul didactic ” ?nume “ a realizat un manual la disciplina ”
?obiect “ in anul ” ?an crlf) )

Această regulă va fi activată dacă în BF există două fapte care să satisfacă condiţiile:
• primul fapt trebuie să aibă 5 câmpuri;
• al doilea fapt trebuie să aibă 11 câmpuri;
• primul fapt trebuie sa aibă ca prim câmp simbolul cadrul-didactic, iar pe poziţia câmpurilor
trei şi patru simbolurile tine şi respectiv disciplina;
• al doilea fapt trebuie să aibă pe poziţia întâi simbolul manualul, pe poziţiile 3 - 5 simbolurile
aparut, in, anul, iar pe poziţiile 7 - 10 simbolurile are, ca, autor, pe;
• câmpul 2 din primul fapt trebuie să coincidă cu câmpul 11 din al doilea fapt;
• câmpul 5 din primul fapt trebuie să coincidă cu câmpul 2 din al doilea fapt.
Rezultă astfel cum prin folosirea repetată a variabilelor în partea de condiţie a regulilor se
pot crea efecte de potrivire complexe.
5. Toate variabilele care au fost folosite în această secţiune au fost variabile simple,
locale şi specificate; celelalte tipuri de variabile vor fi prezentate în secţiunile următoare.

Exerciţii
1. Introduceţi regula de mai sus (R5) într-un fişier, împreună cu un grup de fapte care să
satisfacă partea de condiţie a regulii, în aşa fel încât să obţineţi măcar două activări ale acesteia.
Verificaţi rezultatele prin rulare.

2. Dacă în BR există regula:

(defrule R6
(este-bunic-al ?nume ?nume)
=>
(assert (are-nepot ?nume pe ?nume) ) )

iar în BF există faptele:

25
(este-bunic-al Ion Vasile), (este-bunic-al Ioan Ion), (este-bunic I I),
(este-bunic-al 12 12), (este-bunic-al “Ion” Ion)

câte plasări în agendă ale regulii R6 se obţin? (explicaţi de ce).

3. Dacă în BR există regula:

(defrule R7
(tatal-lui ?copil este ?tata)
(tatal-lui ?tata este ?bunic)
=>
(printout t “Bunicul lui ” ?copil “este” ?bunic crlf)
)

iar în BF încărcaţi conţinutul următorului grup de fapte:

(deffacts relatii-rudenie
(tatal-lui Ion este Mihai)
(tatal-lui Vasile este Mihai)
(tatal-lui Mihai este Andrei)
(tatal-lui Gh este 10)
(tatal-lui Maria este 10)
(tatal-lui 10 este 20)
(tatal-lui Ion este Ion) )
câte activări ale regulii R7 veţi obţine după resetare? (explicaţi de ce).

4. Cu referire la una din observaţiile din referatul anterior, concepeţi regula R8 şi scrieţi
grupul de fapte care să determine următoarea situaţie a agendei:

0 R8 f - 1, f - 2
0 R8 f - 1, f - 1
0 R8 f - 2, f - 1
0 R8 f - 2, f - 2

2. Ştergerea faptelor prin folosirea variabilelor

Adesea este necesar ca un fapt să fie şters din BF în timpul lucrului unui program, prin
acţiunea unei reguli. Un exemplu în acest sens ar fi regula:

(defrule R9 => (retract 0) )

în urma execuţiei regulii obţinându-se ştergerea faptului de la identificatorul f - 0. O asemenea


acţiune va fi foarte rar folosită într-un SE, pentru că ea presupune cunoaşterea exactă a
contorului din identificatorul faptului care se doreşte a fi şters, ceea ce este dificil, ţinând seama
de caracterul dinamic al BF. În schimb există posibilitatea de a folosi variabile în partea de
condiţie a regulilor şi de a obţine ştergerea unui fapt al cărui conţinut se cunoaşte, fără a trebui să
fie precizat identificatorul la care se află acel fapt. Există în acest sens operatorul special <-
care permite legarea valorii contorului din identificatorul unui fapt la o variabilă, în partea de
condiţie a unei reguli; astfel legată, variabila va putea fi apoi folosită în partea de acţiune, într-o
operaţie de ştergere a faptului respectiv. O asemenea situaţie este ilustrată în exemplul următor:

26
(defrule R10
?adresa <- (a 1)
(b 2)
=>
(retract ?adresa)
(assert (c 3) )
)

Partea de condiţie a regulii R10 trebuie înţeleasă în felul următor. Regula cere existenţa în BF a
doua fapte: (a 1) şi (b 2). Dacă regula este activată, variabila ?adresa va fi legată la contorul
din identificatorul faptului (a 1), iar la execuţie, comanda “retract” va determina ştergerea
faptului (a 1) din BF. Este de remarcat că aici programatorului i-a fost necesară numai
cunoaşterea conţinutului faptelor şi nu a locului (adresei) acestora în BF; se ilustrează astfel una
din caracteristicile limbajelor de inteligenţă artificială, aceea că programatorii nu trebuie să se
mai preocupe de calculul adreselor. Pentru conciziunea exprimării vom numi în cele ce urmează
contorul din identificatorul unui fapt ca fiind adresa acelui fapt.
Este admisă şi folosirea variabilelor în tiparul care se foloseşte pentru atribuirea unei
adrese a unui fapt, după cum este posibilă efectuarea mai multor atribuiri de adrese în partea de
condiţie a unei reguli. Iată un exemplu în acest sens:

(defrule R11
?a <- (a ?x)
?b <- (b ?y ?x)
=>
(retract ?a ?b) (printout t ?x “ “ ?y crlf) )

Exerciţiu
Verificaţi prin încărcarea regulii de mai sus în BR şi a faptelor necesare activării în BF, în
ce condiţii este regula plasată în agendă şi care sunt efectele execuţiei ei. Explicaţi funcţionarea.

Observaţii
• Se poate formula o concluzie privind forma comenzii “retract”:
(retract <<<adresa-fapt>>>)
unde adresa-fapt poate fi un întreg pozitiv, reprezentând contorul din identificatorul unui fapt
ce trebuie şters, sau o variabilă legată la adresa unui fapt ce va fi şters.
• Variabilele legate la adrese de fapte nu pot fi folosite pentru adăugarea de fapte în BF cu un
conţinut similar cu acela al faptului având adresa păstrată într-o variabilă. În schimb,
conţinutul unei variabile ce este legată la adresa unui fapt poate fi folosit în partea de acţiune
a unei reguli. În acest sens fie următoarele două reguli:

(defrule R12
?a <- (a ?x)
?b <- (b ?y ?x)
=>
(retract ?b) (assert (fapt ?a)) )

(defrule R13
?ad <- (a ?x)
(fapt ?ad)
=>
(printout t ?x " " ?ad crlf))

27
Exerciţiu
Încărcaţi regulile R12 şi R13 în CLIPS şi experimentaţi funcţionarea lor.

3. Variabile simple nespecificate

Există situaţii când în procesul de potrivire nu ne interesează valoarea unui câmp dintr-un
tipar. Într-un asemenea caz va fi utilă folosirea variabilelor nespecificate (fără nume). Variabila
simplă nespecificată are forma: ?. Fie următorul exemplu. O BF conţine informaţii cu privire la
studenţii unei universităţi. Faptele sunt de forma:

(student <nume> <facultatea> <an-de-studii> <grupa> <domiciliul-stabil>)

Pot fi scrise reguli care să determine faptele ce satisfac anumite condiţii. De exemplu, regula ce
determină toţi studenţii ce au domiciliul în Iaşi este:

(defrule R14
(student ?nume ?facultatea ? ? Iasi)
=>
(printout t ?nume “ de la faculatea ” ?facultatea “ are domiciliul in Iasi” crlf) )

Explicaţia este următoarea. Regula determină toate faptele pentru care câmpul <domiciliul-
stabil> este simbolul Iasi, urmând să afişeze, la execuţie, câmpurile <nume> şi <facultatea>,
fără să intereseze valorile câmpurilor <an-de-studii> şi <grupa>; de aceea în tiparul regulii s-a
folosit în poziţiile care nu interesează variabila nespecificată simplă. În concluzie, variabila
nespecificată simplă se va folosi atunci când potrivirea dintr-un câmp al unui tipar al unei reguli
nu ne interesează şi valoarea respectivă nu trebuie folosită în partea de acţiune a regulii. În
legătură cu variabila ? mai sunt de făcut observaţiile următoare.
• Într-un tipar variabila nespecificată poate fi folosită de mai multe ori; de asemenea ea poate fi
folosită de mai multe ori în mai multe tipare; toate aceste situaţii nu introduc restricţii de
potrivire. De exemplu, în regula de mai sus, folosirea repetată a lui ?, pentru câmpurile 4 şi 5,
nu introduce nicio dependenţă de potrivire între aceste câmpuri.
• Chiar dacă valoarea unui câmp dintr-un tipar nu interesează din punct de vedere al procesului
de potrivire, atunci când acel câmp este necesar în partea de acţiune a regulii, nu se va folosi
variabila nespecificată, ci se va folosi o variabilă specificată. Aşa s-a procedat în cazul de mai
sus cu câmpurile <nume> şi <facultatea>.

Exerciţii
1. Precizaţi care este deosebirea în funcţionare între următoarele două reguli:

(defrule R15 (persoana ?nume are copii ?x) => )


(defrule R16 (persoana ?nume are copii ? x) => )

2. Continuând exemplul de la începutul acestui paragraf, scrieţi regula care să afişeze toţi
studenţii de la facultatea de AC, din anul V. Verificaţi funcţionarea introducând în BF
un grup de fapte potrivite.

4. Variabile multiple specificate şi nespecificate

Variabilele folosite până aici au fost variabile simple (specificate sau nu); aceasta se
referă la aceea că aceste variabile, în procesul de potrivire, ţin locul unui singur câmp şi se pot
lega la un singur câmp.

28
Variabila nespecificată multiplă este $?. Ea poate fi folosită pentru potrivirea a zero sau
mai multe câmpuri dintr-un tipar, câmpuri care nu ne interesează.

Exemplu
Următoarea regulă vrem să fie activată ori de câte ori în BF apar fapte de forma:
(lista <nume-lista> <<element>>)
adică să determine toate faptele care reprezintă liste în BF, indiferent de numărul de elemente ale
listei respective (în particular lista poate fi şi vidă). Această regulă este:

(defrule R17
(lista ?nume $?)
=>
(printout t “S-a gasit lista cu numele “ ?nume crlf)
)

Exerciţiu
Introduceţi regula de mai sus într-un fişier, împreună cu următorul grup de fapte:

(deffacts Liste
(lista L1 3)
(lista L2)
(lista L3 1 3 5)
(lista L4 1 2 3)
(lista L5 1 3 3 5)
(lista L6 3 4 2)
(lista L7 3 4)
(lista L8 1 3 4 2)
(lista L9 1 4 3 2) )

Încărcaţi fişierul şi rulaţi programul; explicaţi funcţionarea.

Există şi variabile multiple specificate; acestea vor fi de forma: $?<nume-variabilă>,


unde nume-variabilă trebuie să fie un simbol care să respecte aceleaşi condiţii ca la variabilele
simple, specificate. Variabilele multiple se vor lega la fel ca variabilele simple, dar se vor putea
lega la 0, 1, 2, ..., n câmpuri. Atunci când se afişează valoarea unei variabile multiple, valoarea
respectivă apare între paranteze.

Exemplu
Regula anterioară poate fi modificată, astfel ca să afişeze elementele din fiecare listă
găsită. Se obţine forma:

(defrule R18
(lista ?nume $?elemente)
=>
(printout t “S-a gasit lista cu numele “ ?nume “ continand elementele “ $?elemente
crlf) )

Mai facem observaţia că variabilele simple şi multiple, specificate sau nu, pot fi folosite
împreună, ceea ce va permite verificarea unor condiţii de potrivire complexe. În ceea ce priveşte
decizia de folosire a unei variabile multiple specificate sau a uneia nespecificate, aceasta se ia
după acelaşi criteriu ca la variabilele simple.

29
Exerciţii
1. Cu referire la acelaşi exemplu din exerciţiul anterior, să se scrie regula care să
găsească toate listele ce conţin elementul 3, indiferent de poziţia acestuia în listă, şi să afişeze
elementele aflate în listă înaintea lui 3 şi după acesta. Verificaţi funcţionarea pe grupul de fapte
“Liste” folosit în exerciţiul anterior şi comentaţi numărul de activări ale regulii.

Observaţie
Este de subliniat că faptul (lista L5 1 3 3 5) a determinat două activări ale regulii
care rezolvă exerciţiul anterior. Am obţinut astfel un exemplu când un acelaşi fapt a determinat
plasarea de două ori pentru o aceeaşi regulă în agendă. Aşa ceva s-a întâmplat deoarece a fost
vorba despre două potriviri diferite.

2. Referitor la acelaşi exemplu cu liste, scrieţi regula care să determine toate listele ce
conţin elementul 3 pe penultima poziţie şi care să mai aibă cel puţin un element în faţa lui 3.

Observaţii în legătură cu variabilele locale

1. Variabilele discutate până aici (simple - multiple, specificate - nespecificate) au fost


variabile locale. Acestea au o anumită semnificaţie numai în interiorul unei reguli, semnificaţia
pierzându-se în afara acesteia. Astfel, dacă de la nivel superficial se dă comanda: ?x, se va afişa
eroare. Atunci când o regulă este activată, o variabilă locală folosită în partea ei de condiţie
păstrează o anumită valoare, obţinută prin procesul de potrivire şi care se menţine până la
sfârşitul execuţiei regulii.
2. Deşi folosirea variabilelor multiple poate fi esenţială în anumite cazuri în conducerea
procesului de potrivire, utilizarea lor excesivă conduce la ineficienţă, deoarece ele determină un
consum important de memorie ca şi creşterea timpului de rulare. Explicaţia este aceea că, de
exemplu, tiparul (a $?x) va fi încercat pe fapte cu 1, 2, ..., n câmpuri, iar variabila multiplă $?x
va trebui să memoreze 0, 1, 2, ... , n câmpuri, în funcţie şi de faptele existente în BF.
Ca o idee generală, o variabilă multiplă specificată sau nespecificată va fi folosită numai
atunci când numărul de câmpuri dintr-un tipar nu se cunoaşte a priori, iar în cazul când acest
număr este cunoscut, se va prefera folosirea repetată a unei variabile simple, chiar dacă aceasta
trebuie scrisă de mai multe ori.
3. O variabilă multiplă specificată, fie aceasta $?x, introdusă în partea de condiţie a unei
reguli, poate fi numită în partea de acţiune a acelei reguli fără semnul $, adică ?x. Semnul $ are
importanţă numai în partea stângă a regulii, acolo unde se produce procesul de potrivire, iar în
partea dreaptă, variabila fiind deja legată, semnul $ nu mai este necesar. Un exemplu în acest
sens este regula:

(defrule R19 (fapt $?a1) => (printout t ?a1 crlf) )

în care, în partea de acţiune a regulii variabila a fost numită fără $.

5. Legarea variabilelor în partea de acţiune a regulilor

În cele prezentate până aici variabilele se legau în partea stângă a regulilor, prin procesul
de potrivire. Dacă este necesar, există şi posibilitatea ca o variabilă să se lege în partea de acţiune
a regulilor (reamintim că legarea în CLIPS este corespondentul atribuirii din limbajele
convenţionale). În acest sens există următoarea comandă, care corespunde unei atribuiri într-un
limbaj procedural:

(bind <variabilă> <expresie>)

30
Drept argument variabilă se poate folosi o variabilă locală, specificată, simplă sau multiplă, sau o
variabilă globală (acestea vor fi prezentate în secţiunea următoare). Argumentul expresie poate fi
o constantă, o funcţie, sau o variabilă globală.

Exerciţiu
Introduceţi următoarea regulă şi comentaţi funcţionarea ei (veţi crea condiţiile ca regula
să fie activată).

(defrule R20
(a ?x ?y ?z ?t)
=>
(bind ?y “as”) (bind ?z (* (+ 1 3) 2)) (bind ?t Ion Ion)
(printout t ?x “ ” ?y “ ” ?z “ ” ?t crlf) )

Pentru a lega o variabilă multiplă la o valoare cu mai multe câmpuri va putea fi folosită o
funcţie care să determine drept rezultat o asemenea valoare. Există mai multe funcţii CLIPS
pentru valori multicâmp; una dintre acestea este:

(mv-append <<element>>)

Efectul acestei funcţii este acela de construire a unei valori multicâmp din elementele care sunt
argumente. Aceste elemente pot fi constante, variabile legate simple sau multiple, sau funcţii
care să determine drept rezultat o valoare cu zero, unul sau mai multe câmpuri. Pentru a vedea
efectul acestei comenzi efectuaţi de la nivel superficial:

(mv-append 1 (+ 12 13) asa (mv-append 2 “an”))

Observaţie
Funcţia “bind” poate fi folosită atât pentru legarea unei variabile care a fost deja folosită
în partea stângă a regulii, cât şi pentru legarea unei variabile nou introdusă în partea de acţiune a
regulii, atunci când o asemenea nouă variabilă este necesară.

Exerciţiu
Introduceţi următoarea regulă şi comentaţi funcţionarea ei (veţi crea condiţiile ca regula
să fie activată prin comanda “assert” sugerată, sau printr-un alt fapt corespunzător, introdus în
BF).

(defrule R21
(fapt ?x $?y ?z) =>
(bind $?y (mv-append Rezultatul este))
(bind ?a 12)
(printout t “noua variabila este a= ” ?a crlf ?y (+ ?x ?z ?a) crlf) )

(assert (fapt 2 as 34 6) )

6. Variabile globale CLIPS

Variabilele globale vor păstra valori atât timp cât interpretorul este lansat. O variabilă
globală se notează prin: ?*<nume-variabilă>*, unde nume-variabilă trebuie să fie un simbol
care să respecte aceleaşi condiţii ca la numele variabilelor locale, specificate. Pentru a putea
folosi o variabilă globală aceasta trebuie în prealabil declarată prin definiţia de construcţie:

31
(defglobal <<<atribuire-variabilă-globală>>>)

unde atribuire-variabilă-globală este de forma: ?*<nume-variabilă>* = <expresie>.


Exemplu

(defglobal ?*g* = 9.8 ?*pi* = 3.14 ?*nume* = Ion)

În acest exemplu s-au definit trei variabile globale care s-au şi iniţializat (legat), conform sintaxei
cerute în comanda “defglobal”.

Observaţii
1. Într-o definiţie “defglobal”, în operaţia de legare trebuie lăsat un spaţiu înainte şi după
semnul =.
2. O variabilă globală se poate lega în definiţia ei şi la o valoare multicâmp, ca în
exemplul următor:

(defglobal ?*nume* = (mv-append Ion Ionescu) )

3. Variabilele globale nu pot fi folosite în partea stângă a regulilor pentru a se potrivi pe


câmpurile din fapte. De exemplu, următoarea regulă este eronată:

(defrule R22 (fapt ?*y*) => )

În schimb variabilele globale pot fi folosite în partea stângă a regulilor în componenţa testelor
asupra câmpurilor (aspect discutat în referatul următor) şi pot fi folosite în partea de acţiune a
regulilor, ca în exemplul următor:

(defglobal ?*g* = 9.8)


(defrule R23 (timp ?t) => (printout t “Viteza este “ (* ?*g* ?t) crlf) )

4. O variabilă globală se poate lega la o nouă valoare faţă de cea din definiţia “defglobal”
prin folosirea funcţiei “bind” (cea descrisă în secţiunea anterioară) folosită la nivel superficial
sau în interiorul unei reguli, în partea de acţiune. Dacă valoarea nu este specificată în funcţia
”bind”, atunci variabila globală este adusă pe valoarea implicită, cea din definiţia ei.

Exemplu
Transmiteţi următoarele comenzi:
(defglobal ?*A* = 12.3) ¶
?*A* ¶
(bind ?*A* “Ura!”) ¶
?*A* ¶
(bind ?*A*) ¶
?*A* ¶

Exerciţiu
Construiţi o regulă în care în partea de acţiune să schimbaţi valoarea unei variabile
globale şi verificaţi funcţionarea.

5. Există o serie de comenzi care ne permit să urmărim variabilele globale. Astfel,


comanda:

(list-defglobals)

32
va determina listarea numelor şi valorilor pentru toate variabilele globale existente în sistem. De
altfel, există şi posibilitatea să creăm o fereastră în care să avem afişate toate variabilele globale,
prin activarea opţiunii respective (Globals) din meniul Window. În plus, variabilele globale pot fi
urmărite prin comanda:

(watch globals)

situaţie în care utilizatorul este anunţat prin mesaje asupra schimbării valorilor variabilelor
globale; această opţiune se poate activa şi din meniul Execution.

Exerciţiu
Introduceţi câteva definiţii de variabile globale, urmăriţi-le şi schimbaţi-le valorile.

6. Comanda “reset” nu şterge variabilele globale. Felul în care acţionează “reset” asupra
valorilor variabilelor globale depinde de starea unui bistabil (“flag”), care poate fi trecut pe
TRUE sau FALSE, prin comanda:

(set-reset-globals {TRUE, FALSE})

Această funcţie determină drept rezultat vechea valoare a bistabilului. Valoarea implicită a
acestuia (cea cu care se intră după lansarea interpretorului) este TRUE. Atunci când bistabilul
este pe TRUE efectul comenzii “reset” este de aducere a variabilelor globale pe valorile lor
iniţiale, cele din definiţia “defglobal”. Dacă bistabilul este pe FALSE atunci comanda “reset” nu
modifică valorile variabilelor globale (acestea rămân la valorile de la ultimele legări). Starea
bistabilului la un moment dat poate fi determinată prin comanda: (get-reset-globals) sau din
meniul Execution activând opţiunea Options şi urmărind marcajul lui “Reset Global Variables”.
De altfel, ajungând în acea fereastră se poate şi schimba starea flag-ului, fără a mai apela la
comanda “set-reset-globals”. Mai menţionăm că în CLIPS există mai multe bistabile care permit
comutarea funcţionării pe varianta convenabilă utilizatorului şi pentru toate vor exista comenzi
de tipul “set” şi “get”, respectiv vor putea fi comutate şi urmărite din anumite meniuri.

Exerciţiu
Urmăriţi efectul comenzii “reset” asupra variabilelor globale în cele două cazuri privind
starea bistabilului de resetare a variabilelor globale.

7. Variabilele globale pot fi eliminate din sistem prin comanda “clear”, cea care şterge
toate informaţiile din sistem sau folosind comanda “undefglobal” având ca parametru numele
unei variabile globale, fără semnele distinctive ? şi *. De exemplu, pentru variabila globală
anterior definită ?*A* putem folosim comanda (undefglobal A). De asemenea, conţinutul
definiţiei unei variabile globale poate fi vizualizat din meniul Browse, opţiunea Defglobal
Manager, de unde se poate obţine şi ştergerea unei variabile globale.

Exerciţii
1. Răspundeţi la următoarele întrebări.
• Care este rolul variabilelor în programarea bazată pe reguli?
• Care este diferenţa între o variabilă locală şi una globală?
• Care este diferenţa între o variabilă specificată şi una nespecificată? Când se va folosi
un tip şi când celălalt ?
• Care sunt diferenţele între următoarele două reguli:
(defrule R24 (a ?x) (b $?y) => (printout t ?x crlf ?y crlf) )
(defrule R25 (a $?x) (b ?y) => (printout t ?x crlf ?y crlf) )

33
• Care sunt faptele pe care le poate şterge în partea de acţiune o regulă?
• Este corectă regula:
(defrule R26 ?a <- (a) => (retract ?a) (bind ?a 10) (printout t ?a
crlf) )
Dacă nu este corectă, care sunt erorile?
• Sunt corecte regulile următoare?
(defrule R27
?a <- (fapt)
?b <- (fapt-adr ?a) => (retract ?b))

(defrule R28
(fapt ?a)
?a <- (fapt-nou ?b) => (retract ?a))

Dacă da, realizaţi condiţiile pentru activarea lor.


• Când va fi activată regula următoare ? Ce rezultat va afişa la execuţie?
(defrule R29 (a ?x) (bind ?x 1) => (printout t ?x crlf) )
• Câte variabile nespecificate pot fi folosite într-o regulă?
• Trebuie scrisă o regulă care să găsească toate listele cu 3 elemente. Care din
următoarele variante este mai bună? De ce?

(defrule R30 (lista ?nume ? ? ?) => (printout t “S-a gasit o lista cu 3 elemente”
crlf) )
(defrule R31 (lista ?nume $?) => (printout t “S-a gasit o lista cu 3 elemente” crlf) )
• Ce efect are comanda “reset” asupra variabilelor globale?

2. Cu referire la exemplul de la începutul secţiunii 3, scrieţi o regulă care să determine


toţi studenţii din anul V, de la facultatea de mecanică; regula va afişa numele studenţilor găsiţi şi
domiciliul lor.

3. Cu referire la exemplul şi exerciţiul din secţiunea 4, scrieţi regulile care să determine:

• două liste ce au acelaşi element pe poziţia a doua;


• două liste ce au aceleaşi elemente;
• două liste ce au un element comun, indiferent de poziţia acestuia.

4. Introduceţi o definiţie de variabilă globală şi scrieţi programul CLIPS cu următoarea


comportare: dacă în BF există faptul (schimbare) atunci valoarea variabilei globale va trebui să
devină simbolul DA, iar dacă în BF există faptul (pastrare) atunci variabila globală respectivă
trebuie adusă la valoarea din definiţia ei (facem presupunerea că faptele (schimbare) şi (pastrare)
nu pot exista simultan în BF).

5. Construiţi un program CLIPS (BF şi BR) care să conţină informaţii cu privire la unele
relaţii de rudenie dintr-o familie şi pe baza acestora să se poată determina prin reguli relaţiile
unchi/mătuşă.

6. O instalaţie este supravegheată de 10 senzori. Fiecare senzor poate fi în starea 0 sau în


starea 1. Scrieţi un program CLIPS (BF şi BR) care să determine un mesaj de avertizare dacă 3
sau mai mulţi senzori sunt pe 0. Programul trebuie să se comporte astfel încât şi atunci când sunt
pe 0 mai mult de 3 senzori mesajul de avertizare să nu fie afişat decât o singură dată.

34
LUCRAREA NR. 4

Realizarea unui program de planificare a acţiunilor în CLIPS; elemente de


interdependenţă între baza de fapte şi baza de reguli
1. O metodologie de realizare a unui program în CLIPS

Pentru a obţine un program CLIPS, mai ales în cazul rezolvării unor probleme complexe,
este util să se folosească următoarea secvenţă de paşi:

Pas 1. Se transpune cunoaşterea asupra metodei de rezolvare a problemei respective


într-un grup de reguli, scrise într-o formă de tip pseudocod.
Pas 2. Se stabileşte forma faptelor (şabloanele pentru acestea), pentru acele fapte ce sunt
necesare pentru a descrie starea iniţială din problema în lucru, eventual forma altor fapte care
trebuie utilizate în rezolvarea problemei, luând în considerare şi forma preliminară a regulilor.
Pas 3. Ţinând seama de forma stabilită a faptelor, de forma regulilor în pseudocod şi de
sintaxa limbajului, se scrie forma finală a regulilor.

De obicei este necesar ca acest ciclu să se repete de mai multe ori, până se ajunge la o
variantă optimă. De asemenea trebuie menţionat că ordinea primilor doi paşi nu este una strictă;
vom avea în vedere interdependenţa cunoscută între fapte şi reguli, şi vom putea pleca fie de la
forma principială a regulilor (partea de condiţie a acestora), fie de la forma faptelor. Această
metodă va fi utilizată în continuare pe un caz concret, constând în planificarea acţiunilor unui
robot.

2. Planificarea acţiunilor unui robot într-un mediu format din corpuri cu forme
geometrice simple

Mediul de lucru (universul de discurs)


D este cel din Fig. 1. Singurele elemente de interes
A sunt blocurile (corpuri având formă de tip
E paralelipiped) şi robotul care le mişcă. Blocurile
B
sunt aşezate unele peste altele, pe podea, în
C F diverse configuraţii. Pentru o primă soluţie a
podea
problemei vom face o serie de ipoteze
Fig. 1. O problemă în lumea blocurilor simplificatoare.
Acestea sunt:

• un singur bloc va fi stivuit deasupra altuia;


• robotul poate mişca un singur bloc o dată; ne interesează numai nivelul superior al sistemului
de comandă a robotului, fără detaliile privind materializarea fiecărei mişcări pe care acesta o
efectuează;
• pe podea pot fi puse oricâte blocuri;
• scopul dat robotului va fi acela de obţinere a anumitor configuraţii (anumitor stive);
• va fi admis numai un scop simplu, exprimabil printr-o singură condiţie; pentru situaţia din
Fig. 1, un asemenea scop ar fi: blocul C să fie deasupra blocului E.
• nu va fi admis un scop care este deja îndeplinit (de exemplu, pentru cazul din Fig. 1 nu se va
admite ca scop: blocul D să fie deasupra blocului E); această condiţie restrictivă va putea fi
eliminată prin folosirea testelor în CLIPS.

35
Parcurgem primul pas al metodei date în secţiunea anterioară, scriind regulile necesare în
rezolvarea problemei într-o primă formă, de tip pseudocod.

1) În situaţia în care se cere printr-un scop ca un bloc să fie deasupra altui bloc şi atât
blocul care se mută (blocul sursă) cât şi cel destinaţie sunt libere, adică se află în vârful stivelor
din care fac parte, atunci robotul poate face mişcarea de mutare. Rezolvarea acestei situaţii este
concretizată în următoarea regulă, numită Mutare-directă:

Dacă scopul este de a muta blocul ?sursa pe blocul ?destinatie şi blocul ?sursa este
liber şi blocul ?destinatie este liber atunci mută blocul ?sursa pe blocul ?destinatie.

În exprimarea de mai sus am folosit notarea variabilelor din CLIPS.


2) Deoarece podeaua are o situaţie deosebită faţă de blocuri (poate primi deasupra ei
oricâte blocuri), vom prevedea o regulă distinctă pentru mutarea unui bloc pe podea, numită
Mutare-pe-podea.

Dacă scopul este de a muta blocul ?sursa pe podea şi blocul ?sursa este liber
atunci mută blocul ?sursa pe podea.

În acest fel regula Mutare-directă va fi de folosit în cazul în care sursa şi destinaţia sunt blocuri,
iar regula Mutare-pe-podea va fi de folosit atunci când destinaţia nu este un bloc, ci podeaua.
3) O regulă care să permită eliberarea unui bloc care nu este liber şi care printr-un scop
existent urmează a fi mutat este următoarea (o numim regula Eliberare-sursă):

Dacă scopul este de a muta blocul ?x şi blocul ?x nu este liber


şi blocul ?y este pe blocul ?x atunci creează scopul de a muta blocul ?y pe podea.

În legătură cu regula de mai sus este de observat că destinaţia blocului ?x nu interesează în


această regulă şi de aceea ea nu apare, iar faptul că blocul ?x nu este liber înseamnă că el nu este
în vârful stivei sale. Regula scrisă, conform felului în care lucrează un program bazat pe reguli,
se va aplica în mod repetat atunci când mai multe blocuri sunt deasupra unui bloc, creând toate
scopurile necesare (regula va fi activată în mod recursiv).
4) O regulă similară cu aceea de mai sus este cea de eliberare a destinaţiei unui scop
(regula Eliberare-destinaţie):

Dacă scopul este de a muta ceva pe blocul ?x şi blocul ?x nu este liber


şi blocul ?y este pe blocul ?x atunci creează scopul de a muta blocul ?y pe podea.

În această regulă nu a interesat care este blocul care urmează a fi mutat pe blocul ?x.

După ce a fost stabilită forma principială a regulilor putem trece la pasul al doilea, acela
de fixare a formei faptelor prin care să se descrie starea iniţială din problema respectivă. Ţinând
seama de Fig. 1, de felul în care am conceput cele patru reguli preliminare, rezultă ca necesare
următoarele tipuri de fapte.
a) Poziţia relativă a blocurilor o vom preciza prin fapte respectând următorul şablon:

(pe <element> <element>)

Exemple de folosire a acestui tip de fapte pentru problema noastră sunt: (pe A B) (pe F
podea) (pe nimic D).

36
b) Cuvintele nimic, podea, trebuie să aibă un înţeles deosebit faţă de cuvintele care
reprezintă blocurile, pentru a nu putea fi interpretate ca blocuri. De aceea, ca măsură de
precauţie, vom preciza blocurile prin fapte de forma:

(este-bloc <nume-bloc>)

În problema în lucru vom avea: (este-bloc A), (este-bloc B), ş.a.m.d.


c) În regulile stabilite anterior este necesară precizarea scopurilor; în acest sens vom
folosi fapte de forma:

(scop muta <bloc> pe <element>)

De exemplu, scopul din problema propusă va fi de forma: (scop muta C pe E). În toate
aceste tipare am folosit o serie de cuvinte cu rol ajutător, astfel ca BF să fie mai uşor de urmărit
şi înţeles.

Trecem acum la pasul al treilea al metodei, acela în care ţinând seama de cele stabilite în
cele două etape anterioare, cât şi de sintaxa limbajului, scriem forma finală a regulilor.
Regula Mutare-directă, scrisă în CLIPS, este:

(defrule mutare-directa “aceasta regula intra in executie cand sursa si destinatia sunt
libere si destinatia este un bloc”
?scop <- (scop muta ?bloc1 pe ?bloc2)
(este-bloc ?bloc1)
(este-bloc ?bloc2)
(pe nimic ?bloc1)
?stiva1 <- (pe nimic ?bloc2)
?stiva2 <- (pe ?bloc1 ?bloc3)
=>
(retract ?scop ?stiva1 ?stiva2)
(assert (pe ?bloc1 ?bloc2) (pe nimic ?bloc3) )
(printout t “blocul ” ?bloc1 “ este mutat pe blocul ”
?bloc2 crlf) )

Se pot face următoarele observaţii în legătură cu această regulă. Ea are mai multe roluri.
• Să determine momentul când o mişcare de mutare a unui bloc poate fi făcută de către robot, în
conformitate cu scopurile existente în BF şi cu condiţiile necesare pentru efectuarea mişcării.
Astfel primul tipar al regulii verifică existenţa scopului care va declanşa mişcarea. Tiparele
doi şi trei se referă la condiţia ca valorile păstrate în variabilele ?bloc1 şi ?bloc2 să se refere
în adevăr la blocuri, iar următoarele două conţin condiţiile ca blocurile care sunt implicate în
mişcare (blocul sursă şi cel destinaţie) să fie în vârful stivelor din care fac parte. Ultimul tipar
al regulii are importanţă pentru procesul de actualizare a BF.
• Să efectueze o actualizare a BF. De altfel, acesta este un principiu general; ori de câte ori un
SE/program bazat pe reguli determină o modificare în universul de discurs, va trebui făcută şi
actualizarea corespunzătoare în BF, în ceea ce priveşte modelul mediului (modelul de
reprezentare a cunoaşterii) aflat în BF. În acest fel, BF va păstra în permanenţă situaţia
curentă, corectă, din universul de discurs. Concret, în exemplul considerat, actualizarea BF se
referă la ştergerea faptelor care nu mai sunt actuale: scopul care a declanşat regula şi care
fiind îndeplinit trebuie eliminat din BF şi faptele care se referă la starea anterioară a celor
două stive care sunt afectate de mişcarea efectuată (stiva din care se ia blocul având numele în
variabila ?bloc1 şi stiva în care se pune acest bloc, peste blocul ?bloc2). Pe lângă ştergerea
faptelor care nu mai trebuie să existe în BF, actualizarea se referă şi la introducerea a două noi

37
fapte în BF, în partea de acţiune a regulii, în conformitate cu noua situaţie a stivelor afectate
de mişcarea respectivă.
Mai menţionăm că programul nefiind conectat efectiv cu un robot, comanda mişcării este
înlocuită cu mesajul transmis pe terminal, prin partea de acţiune a regulii.
O formă similară are regula Mutare-pe-podea:

(defrule mutare-pe-podea “aceasta regula intra in executie cand un bloc trebuie mutat
pe podea si blocul respectiv este liber”
?scop <- (scop muta ?bloc1 pe podea)
(este-bloc ?bloc1)
(pe nimic ?bloc1)
?stiva <- (pe ?bloc1 ?bloc2)
=>
(retract ?scop ?stiva)
(assert (pe ?bloc1 podea) (pe nimic ?bloc2) )
(printout t “blocul ” ?bloc1 “ este mutat pe podea” crlf) )

Părţile de condiţie şi de acţiune ale acestei reguli sunt uşor de urmărit, explicaţiile fiind
similare cu acelea de la regula anterioară, cu observaţia că regula Mutare-pe-podea are mai
puţine tipare şi acţiuni, deoarece în ceea ce priveşte podeaua nu mai sunt necesare verificări
(conform uneia din specificaţii, podeaua poate primi oricâte blocuri).
Pentru regula Eliberare-sursă, plecând de la forma în pseudocod se ajunge la următoarea
formă în CLIPS:

(defrule eliberare-sursa “aceasta regula intra in executie cand un bloc trebuie mutat
undeva si deasupra sa se afla alt bloc”
(scop muta ?bloc1 pe ?)
(este-bloc ?bloc1)
(pe ?bloc2 ?bloc1)
(este-bloc ?bloc2)
=>
(assert (scop muta ?bloc2 pe podea) ) )

Sunt uşor de urmărit părţile de condiţie şi de acţiune ale acestei reguli. Este de observat
că destinaţia mutării blocului ?bloc1 nu este necesară în această regulă şi de aceea s-a folosit în
tiparul respectiv variabila simplă nespecificată.
Ca o remarcă privind cele patru reguli care rezolvă problema, ele pot fi grupate în două
categorii. Regulile Mutare-directă şi Mutare-pe-podea sunt cele care dau planul de lucru pentru
robot, în timp ce regulile Eliberare-sursă şi Eliberare-distinaţie nu transmit comenzi spre robot, ci
doar adaugă fapte (scopuri) în BF, creând condiţiile celorlalte reguli pentru determinarea soluţiei.

Exerciţiu
1. Să se scrie în CLIPS regula Eliberare-distinaţie (se va lucra într-un fişier).
2. Să se scrie, în acelaşi fişier, şi celelalte trei reguli necesare programului, ca şi grupul
de fapte corespunzător stării iniţiale (cea din Fig. 1) şi să se experimenteze funcţionarea
programului.
3. Verificaţi starea BF după rularea programului, observaţi faptul incorect care apare şi
modificaţi programul în aşa fel încât să nu mai apară eroarea respectivă.

O a doua variantă a programului de planificare a acţiunilor robotului se va putea obţine


considerând turnurile în care se află blocurile ca nişte structuri de date de tip stivă. În acest sens
este de interes scrierea programului CLIPS care să manevreze asemenea structuri de date.

38
3. Implementarea unei stive în CLIPS

Datorită simplităţii problemei nu vom mai parcurge în mod explicit cele trei etape ale
metodei date în secţiunea 1. O stivă va fi reprezentată în BF folosind următorul tipar:

(stiva <<element-stiva>>)

Odată stabilită forma acestui fapt vor fi uşor de scris regulile care să realizeze operaţiile
de prelucrare a stivei - introducere şi scoaterea de elemente din aceasta. Deoarece numărul de
elemente din stivă nu se cunoaşte a priori, în scrierea regulilor vom folosi variabilele multiple.
Pentru regula de introducere în stivă considerăm că va exista un fapt de declanşare a acestei
operaţii, de forma:

(push <valoare>)

unde câmpul al doilea va conţine valoarea ce se doreşte a se adăuga în stivă. Astfel, regula
(scrisă direct în CLIPS) rezultă de forma:

(defrule push-valoare “regula de introducere in stiva”


?adresa <- (push ?valoare)
?stiva <- (stiva $?rest)
=>
(retract ?adresa ?stiva)
(assert (stiva ?valoare $?rest))
(printout t “S-a introdus in stiva valoarea: ” ?valoare crlf) )

Pentru operaţia de extragere din stivă trebuie să concepem două reguli care să intre în
acţiune în cele două cazuri posibile: se cere o extragere din stiva ce conţine măcar un element şi
respectiv cazul în care se cere extragerea dintr-o stivă vidă. Pentru prima situaţie regula este:

(defrule pop-stiva-plina “regula de extragere dintr-o stiva ce contine macar un


element”
?adresa <- (pop)
?stiva <- (stiva ?valoare $?rest)
=>
(retract ?adresa ?stiva)
(assert (stiva $?rest))
(printout t “Valoarea extrasa din stiva este: ” ?valoare crlf) )

În regula de mai sus faptul (pop) va apare în BF atunci când utilizatorul doreşte
efectuarea unei extrageri din stivă; evident, am presupus că în BF există o singură stivă.

Exerciţiu
1. Scrieţi regula pentru cazul în care se cere extragerea dintr-o stivă vidă (observaţi
caracterul disjunct al acestei reguli cu cea scrisă mai sus, în sensul neactivării
simultane a celor două reguli).
2. Rescrieţi regulile de extragere din stivă în aşa fel încât utilizatorul să fie anunţat atunci
când se extrage ultimul element din stivă.
3. Rescrieţi regulile de lucru cu o stivă pentru cazul în care în BF se pot afla mai multe
stive.
4. Scrieţi regulile pentru lucrul cu o structură de date de tip coadă.

39
Pe baza elementelor prezentate privind lucrul cu stive putem găsi o nouă soluţie la
problema de planificare a acţiunilor robotului. Astfel fiecare turn de blocuri poate fi modelat
printr-o structură de date de tip stivă, caz în care operaţiile de mutare se vor reduce la operaţii
asupra structurilor de date respective. În acest fel se simplifică şi forma faptelor, ajungând pentru
cazul din Fig. 1 la următorul grup de fapte reprezentând starea iniţială:

(deffacts stare-initiala
(stiva A B C)
(stiva D E F)
(scop muta C pe E) )

Simplificarea provine din aceea că nu a mai fost necesar să reprezentăm explicit elementul care
este în vârful unui turn, el fiind implicit primul din stiva respectivă, şi nici elementul aflat pe
podea, acesta fiind ultimul din stivă; de asemenea podeaua nu a mai fost reprezentată explicit. În
plus, am presupus că în stive nu apar decât elemente care sunt blocuri, astfel că nu am mai
introdus fapte speciale care să ateste calitatea unui obiect de a fi bloc.
Regula Mutare-directă devine:

(defrule mutare-directa
?scop <- (scop muta ?bloc1 pe ?bloc2)
?stiva1 <- (stiva ?bloc1 $?rest1)
?stiva2 <- (stiva ?bloc2 $?rest2)
=>
(retract ?scop ?stiva1 ?stiva2)
(assert (stiva $?rest1) (stiva ?bloc1 ?bloc2 $?rest2) )
(printout t “blocul ” ?bloc1 “ este mutat pe blocul ” ?bloc2 crlf) )

Exerciţiu
Scrieţi celelalte trei reguli de planificare a acţiunilor unui robot în noua variantă, aceea a
lucrului cu stive.

4. Elemente de interdependenţă între baza de fapte şi baza de reguli

Interpretorul CLIPS posedă câteva bistabile care permit utilizatorului să aleagă între mai
multe variante în funcţionare, în funcţie de specificul problemei pe care o rezolvă (unul din
aceste bistabile a fost deja discutat, cel referitor la variabilele globale). Aceste opţiuni se găsesc
în meniul Execution, opţiunea Options. Există două opţiuni cu privire la BF şi BR: comportarea
BF în ceea ce priveşte faptele duplicat şi respectiv felul în care regulile sunt sensibile la
momentul introducerii faptelor în BF.

Comportarea BF în ceea ce priveşte faptele duplicat


Există un bistabil care determină comportarea BF în ceea ce priveşte faptele duplicat.
Acesta poate fi modificat prin comanda:

(set-fact-duplication {TRUE, FALSE})

sau din meniul Execution, conform menţiunii anterioare (rezultatul acestei comenzi, ca şi la toate
comenzile de tip “set”, este vechea valoare a bistabilului respectiv). Valoarea implicită a
bistabilului este FALSE, situaţie în care un fapt, cu un anume conţinut, este admis o singură dată
în BF. În acest caz, dacă de la nivel superficial sau din partea de acţiune a unei reguli se execută
o comandă de introducere în BF a unui fapt care deja există în BF, atunci faptul respectiv nu mai
este înscris în BF, fără ca interpretorul să semnaleze situaţia respectivă. În situaţia în care

40
bistabilul este pus pe TRUE, un fapt poate fi introdus în BF de ori câte ori, cu consecinţele care
pot apare în ceea ce priveşte procesul de potrivire. Starea bistabilului la un moment dat se poate
vedea din meniul Execution, opţiunea Options sau cu comanda:

(get-fact-duplication)

Sensibilitatea regulilor la momentul introducerii faptelor în BF


Există un bistabil care controlează această sensibilitate. El poate fi modificat prin
comanda:

(set-incremental-reset {TRUE, FALSE})

Valoarea implicită a bistabilului este TRUE, situaţie în care regulile văd faptele (în sensul că
sunt activate de ele) indiferent de momentul în care au fost introduse faptele în BF. Dacă
bistabilul este pus pe FALSE, atunci regulile nu mai văd, în sensul că nu sunt activate decât de
faptele care au fost introduse în BF după definiţia lor. Starea bistabilului poate fi determinată din
meniul Execution sau prin comanda:

(get-incremental-reset)

Mai menţionăm că starea acestui bistabil va putea fi modificată numai dacă în BR nu există nicio
regulă.
Pentru a verifica influenţa acestor două bistabile se va experimenta folosind următorul
exemplu, introdus de la nivel superficial (în timpul experimentelor se va urmări şi agenda):

(get-fact-duplication)¶
(get-incremental-reset)¶
(set-incremental-reset FALSE)¶
(assert (a))¶
(assert (a))¶
(defrule r (a) => (assert (b)) )¶
(retract 1)¶
(assert (a))¶
(run)¶
(set-incremental-reset TRUE)¶
(clear)¶
(set-incremental-reset TRUE)¶
(set-fact-duplication TRUE)¶
(assert (a) )¶
(defrule r (a) => (assert (b)) )¶
(assert (a) )¶
(run)¶

Se vor explica rezultatele obţinute.

Exerciţiu

Scrieţi programul CLIPS care să determine toate permutările unei mulţimi cu trei elemente.

41
LUCRAREA NR. 5

Realizarea testelor în CLIPS


În programele prezentate până aici, testele care se puteau face în CLIPS, în partea de
condiţie a regulilor, erau de următoarele două tipuri:

• testarea valorii unui câmp dintr-un tipar, în sensul ca acesta să fie identic cu o valoare
constantă; de exemplu, în regula (defrule R1 (a 12) => ) se cere ca faptul care satisface
tiparul regulii să aibă primul câmp egal cu constanta a, iar al doilea câmp egal cu constanta 12;
• testarea ca valoarea a două sau mai multe câmpuri din acelaşi tipar, sau din tipare diferite
din partea de condiţie a unei aceleiaşi reguli să fie egale, prin folosirea repetată a unei aceleiaşi
variabile specificate; de exemplu, în regula (defrule R2 (a ?x ?x) (b ?x ?x) => ) se cere
ca faptele care satisfac cele două tipare să aibă câmpurile doi şi trei egale între ele şi egale în cele
două fapte, valoarea respectivă urmând a se lega la variabila ?x.

În afara acestor tipuri de teste, CLIPS mai oferă şi alte posibilităţi prezentate în
continuare.

1. Operaţii logice asupra câmpurilor dintr-un tipar

În partea de condiţie a unei reguli, asupra câmpurilor unui tipar se vor putea efectua
operaţiile logice ŞI, SAU, NEGARE. Le vom ilustra folosind un exemplu utilizat şi anterior: o
BF conţine informaţii cu privire la studenţii unei universităţi, prin fapte respectând următorul
tipar:

(student <nume> <facultatea> <an-de-studii> <grupa> <domiciliul-stabil>) (1)

a) Operaţia de negare se simbolizează prin: ~ (tilda).


Regula care determină toţi studenţii de la facultatea de AC, care nu au domiciliul în Iaşi
este:

(defrule R3 (student ?nume AC ? ? ~Iasi)


=> (printout t ?nume “ este student la AC si nu are domiciliul in Iasi” crlf) )

În regula respectivă nu ne-au interesat valorile câmpurilor în care este precizat anul de studii şi
grupa, astfel că am folosit pentru poziţiile respective variabila simplă nespecificată.

b) Operaţia SAU se simbolizează prin: | (bara verticală).


Regula care determină toţi studenţii care au domiciliul în Iaşi sau Paşcani este:

(defrule R4 (student ?nume AC ? ? Iasi | Pascani)


=> (printout t ?nume “ este student la AC si are domiciliul in Iasi sau Pascani” crlf) )

cu observaţia că înainte şi după simbolul | poate fi sau nu lăsat un spaţiu, deoarece | este el însuşi
un delimitator.

c) Operaţia ŞI se simbolizează prin: & (ampersand).


O operaţie de conjuncţie între două constante, efectuată pentru un câmp al unui tipar,
determină o eroare de sintaxă, întrucât conduce la obţinerea unei reguli care nu va fi niciodată
activată, adică o regulă inutilă. De exemplu, regula următoare:

42
(defrule R5 (student ?nume AC ? ? Iasi & Pascani) => )

ar fi ”activată” dacă în BF ar putea exista un fapt de forma (1) care să aibă pe ultimul câmp şi
valoarea Iasi şi valoarea Pascani, ceea ce evident este imposibil.
Totuşi operaţia ŞI este de utilizat în testele asupra câmpurilor tiparelor în următoarele două
tipuri de situaţii.
a) În cazul în care ne interesează să realizăm un test asupra unui câmp al unui tipar, iar
valoarea câmpului trebuie să fie şi păstrată într-o variabilă, pentru a mai putea fi folosită în
partea de condiţie şi/sau de acţiune, vom conecta variabila cu testul efectuat prin operatorul &.
Asemenea situaţii sunt ilustrate prin următoarele două reguli:

(defrule R6 (student ?nume AC ? ? ?x & ~Iasi)


=>
(printout t ?nume “ este student la AC si nu are domiciliul in Iasi, ci in localitatea ” ?x
crlf))

(defrule R7 (student ?nume AC ? ? ?x & Iasi | Pascani)


=> (printout t ?nume “ este student la AC si are domiciliul in ” ?x crlf) )

Regula R6 determină toţi studenţii de la AC care nu au domiciliul în Iaşi şi, spre deosebire de
regula R3, în partea de acţiune afişează localitatea de domiciliu, deoarece aceasta a fost păstrată
în variabila ?x. Regula R7 determină toţi studenţii de la AC care au domiciliul în Iaşi sau
Paşcani, la fel ca regula R4, dar în plus afişează localitatea de domiciliu, a cărei valoare este
păstrată în variabila ?x.
Un alt exemplu de folosire este următorul, în care regula R8 determină doi studenţi de la
facultatea de AC, care nu locuiesc în Iaşi, sunt din grupe diferite, din anul V, afişând numele
studenţilor respectivi şi grupele din care fac parte:

(defrule R8
(student ?nume1 AC V ?x ~Iasi)
(student ?nume2 AC V ?y&~?x ~Iasi)
=> (printout t ?nume1 “ de la grupa ” ?x “ si ” ?nume2 “ de la grupa ” ?y
“ satisfac cerintele impuse” crlf) )

De observat că în această regulă operatorul de negare a fost folosit şi în faţa unei variabile
(apare ~?x în tiparul al doilea); o asemenea utilizare presupune ca variabila respectivă să fie deja
legată şi semnificaţia este aceea de a se admite o potrivire la o valoare diferită de valoarea
variabilei. De exemplu, faptele următoare:

(student Ionescu AC V 1401 Vaslui)


(student Popescu AC V 1402 Brasov)

activează regula R8, cu variabilele ?x şi ?y legate la valorile 1401 şi respectiv 1402, în timp ce
faptele:

(student Ionescu AC V 1401 Vaslui)


(student Popescu AC V 1401 Brasov)

nu activează regula, deoarece nu se respectă condiţia ca valoarea lui ?y să fie diferită de valoarea
lui ?x.
În concluzie, în cazul tratat la punctul a) operatorul & asigură legarea unei variabile într-un
tipar al unei reguli, legare care să urmărească şi satisfacerea unei anumite condiţii.

43
b) În cazul în care trebuie să realizăm teste compuse, în care să fie combinate mai multe
operaţii logice, poate apare necesitatea folosirii operaţiei ŞI. De exemplu, regula de determinare
a studenţilor care să nu aibă domiciliul nici în Iaşi şi nici în Paşcani este:

(defrule R9 (student ?nume AC ? ? ?x & ~Iasi & ~Pascani) =>


(printout t ?nume “ este student la AC si nu are domiciliul in Iasi sau Pascani, ci
in ” ?x crlf)
)

Se observă că în cazul de mai sus operaţia & a fost folosită atât pentru legarea variabilei ?x la
rezultatul unui test, cât şi pentru obţinerea conjuncţiei între cele două negaţii.
Operaţiile logice asupra câmpurilor tiparelor se pot combina, creând posibilitatea testării
unor condiţii complexe.

Exemplu
Să se determine din BF cu informaţii asupra studenţilor, dacă există două persoane ce
satisfac următoarele condiţii:
• prima persoană este student la AC în grupa 1401 sau 1402 şi nu locuieşte în Iaşi;
• a doua persoană este student la AC, în aceeaşi grupă cu prima persoană şi locuieşte
sau în Bucureşti sau în aceeaşi localitate ca şi prima persoană.

Regula ce rezolvă problema este:

(defrule R10
(student ?nume1 AC ? ?gr&1401|1402 ?dom&~Iasi)
(student ?nume2&~?nume1 AC ? ?gr ?dom | Bucuresti)
=>
(printout t “Studentii ” ?nume1 “ si ” ?nume2 “ din grupa ” ?gr
“ indeplinesc conditiile cerute” crlf) )

Este de observat că în al doilea tipar s-a verificat şi condiţia ca a doua persoană să aibă un
nume diferit faţă de prima persoană, în acest fel împiedicând activarea regulii cu cele două tipare
potrivite pe acelaşi fapt. De reţinut această modalitate de a nu permite ca un acelaşi fapt să
verifice două tipare dintr-o regulă – verificarea unui câmp de identificare unic pentru fiecare
fapt, atunci când ne interesează aşa ceva; acest aspect a apărut şi într-o serie de probleme din
referatele anterioare.

Exerciţii
1. Experimentaţi regula R10 creând o BF cu fapte care să respecte tiparul (1) şi în aşa fel
încât regula să fie plasată în agendă de câteva ori.
2. Dacă în BF cu informaţiile despre studenţi apare un fapt de forma:

(caut-student <grupa> <domiciliul>)

în care în câmpurile doi şi trei sunt precizate grupa, respectiv domiciliul dorit, să se scrie
programul care să găsească în BF studenţii care satisfac condiţia de domiciliu cerută şi nu
satisfac condiţia privind grupa, studenţii care satisfac condiţia de grupă cerută şi nu satisfac
condiţia privind domiciliul, şi respectiv studenţii care satisfac ambele condiţii.
3. Să se scrie programul care să determine existenţa informaţiilor despre doi studenţi care
să satisfacă următoarele condiţii:

a) primul este din anul III sau IV, nu este de la facultatea de AC şi are domiciliul în Iaşi;

44
b) al doilea este din anul II şi este de la aceeaşi facultate ca şi primul student, sau este din
aceeaşi grupă cu primul student, dacă acesta (primul student) satisface condiţiile de la a)
şi în plus este de la facultatea de textile.

2. Predicate în CLIPS

În CLIPS noţiunea de predicat are aceeaşi semnificaţie ca în logică: un predicat este o


funcţie care are drept rezultat o valoare de adevăr: adevărat sau fals. În CLIPS, valoarea fals este
reprezentată de simbolul FALSE, iar valoarea adevărat este reprezentată de orice altă constantă,
diferită de FALSE. Atunci când interpretorul CLIPS afişează o valoare de adevăr foloseşte
TRUE pentru adevărat şi FALSE pentru fals.
Predicatele pentru operaţiile logice ŞI, SAU, NEGAŢIE sunt în CLIPS:

(and <<<predicat>>>)
(or <<<predicat>>>)
(not <predicat>)

Ţinând seama de felul în care interpretează CLIPS valorile de adevăr (orice este diferit de
FALSE, este adevărat), rezultă că orice funcţie CLIPS care întoarce un rezultat, sau orice
constantă CLIPS, poate fi privită şi ca predicat. În acest sens, evaluaţi de la nivel superficial
următoarele expresii şi explicaţi rezultatele obţinute:

(or 12 “logica”)
(and -90 FALSE)
(and (facts) (printout t “Terminare afisare!” crlf))
(not -56)

De obicei, operaţiile logice vor fi utilizate împreună cu o serie de predicate specializate.


Astfel, în efectuarea testelor vor fi utile următoarele predicate de testare a tipului: numberp -
pentru tipul număr, symbolp - pentru tipul simbol, stringp - pentru tipul şir de caractere, integerp
- pentru tipul număr întreg, floatp – pentru tipul număr real, evenp - pentru tipul număr par,
oddp - pentru tipul număr impar. Toate aceste predicate au aceeaşi sintaxă, ilustrată pe cazul lui
“numberp”:

(numberp <argument>)

care va determina rezultatul TRUE dacă argumentul are ca valoare un număr şi rezultatul
FALSE în caz contrar.
Pentru efectuarea de comparaţii sunt utile următoarele predicate: eq - testarea egalităţii,
neq - testarea ne-egalităţii, = - testarea egalităţii, <> - testarea inegalităţii, >= - mai mare sau
egal, > - mai mare, <= - mai mic egal, < - mai mic. Toate aceste predicate au aceeaşi sintaxă,
ilustrată pe exemplul următor:

(eq <argument1> <argument2>)

care va determina rezultatul TRUE dacă valorile celor două argumente sunt egale. Predicatele eq
şi neq pot fi folosite pentru testarea a două valori oarecare, în timp ce toate celelalte pot fi
folosite numai pentru cazul în care valorile argumentelor care se testează sunt numere. Dacă
suntem în situaţia de testare a egalităţii a două valori al căror tip nu îl cunoaştem, va trebui să
folosim predicatul eq; în schimb, atunci când suntem siguri că valorile testate sunt numere vom
folosi predicatul =, care determină o execuţie mai rapidă decât eq.

45
În CLIPS predicatele vor fi folosite în reguli, în partea de condiţie a acestora. Pentru
aceasta va fi utilizat un tipar de test, realizat conform sintaxei:

(test <predicat>)

Se creează în acest fel un tipar în partea stângă a unei reguli care se va considera satisfăcut dacă
rezultatul pe care îl furnizează argumentul predicat este TRUE.

Exemple
1. Fie următoarea definiţie de regulă:

(defrule R11
(a 1)
(b ?x)
(test (> ?x 0))
=>
(printout t ?x crlf))

Regula R11 va fi activată dacă în BF există faptul (a 1) şi un alt fapt care să aibă drept
prim câmp simbolul b; al doilea câmp din faptul respectiv se leagă la variabila ?x. În
conformitate cu tiparul de test, regula este activată numai dacă, în plus faţă de cele spuse,
valoarea legată la variabila ?x este un număr (predicatul > dă eroare dacă argumentele sale nu
sunt numere) strict mai mare ca 0.
2. Regula următoare este una activată de fapte cu trei câmpuri, în care: primul câmp este
simbolul fapt, al doilea trebuie să fie un şir de caractere, iar al treilea câmp trebuie să fie un
număr impar, mai mare sau egal cu 5 şi strict mai mic ca 89:

(defrule R12
(fapt ?x ?y)
(test (and (stringp ?x)
(oddp ?y)
(>= ?y 5)
(< ?y 89)
)
)
=>
(printout t ?x “ si ” ?y “ satisfac restrictiile cerute” crlf)
)

Ca observaţii importante, este de remarcat felul în care mediul CLIPS nu permite existenţa
în BF a faptelor care au în primul câmp simbolul test, acesta fiind un simbol rezervat, având
unicul scop de a introduce un tipar de test. În plus, într-o regulă pot exista oricâte tipare de test.
Este de remarcat şi faptul că în CLIPS primul câmp al unui tipar dintr-o regulă trebuie să
fie o constantă de tip simbol. Aceasta înseamnă că nu putem începe un tipar al unei reguli cu o
variabilă, asupra căreia să putem efectua un test. De exemplu, următoarea regulă nu este
acceptată de sintaxa CLIPS:

(defrule R13
(?x ?y data)
(test (numberp ?x))
=>
(printout t ?x ” ” ?y crlf) )

46
Concluzia este aceea că regulile şi faptele trebuie astfel concepute şi corelate încât pe
prima poziţie în fapte să fie valori fixate în aşa fel încât să nu trebuiască a fi supuse unor teste în
reguli, ci furnizate ca atare; aşa cum s-a arătat şi anterior, o variantă des utilizată va fi aceea în
care faptele vor avea pe prima poziţie numele relaţiei la care acestea se referă, nume care va fi un
simbol folosit ca atare în tiparele din partea stângă a regulilor.

Exerciţii
1. Scrieţi regula care să determine faptele din BF care sunt de forma (data <valoare1>
<valoare2>), în care sau câmpurile doi şi trei sunt numere ce satisfac condiţia că valoare1 este un
multiplu al lui valoare2, sau câmpurile doi şi trei sunt două simboluri egale. Pentru rezolvare va
putea fi utilizată funcţia:

(mod <argument1> <argument2>)

care determină restul împărţirii întregi a primului argument la cel de-al doilea argument (evident,
cele două argumente ale funcţiei trebuie să fie numere).
2. Scrieţi regula care să determine perechile de fapte din BF ce sunt ambele de câte trei
câmpuri şi satisfac condiţiile:
• primul câmp din ambele fapte este simbolul data;
• al doilea câmp din primul fapt trebuie să fie un număr şi să nu fie egal cu al treilea
câmp din al doilea fapt;
• al treilea câmp din primul fapt trebuie să nu fie număr şi să fie egal cu al doilea câmp
din al doilea fapt.

3. Operatorul de restricţie asupra câmpurilor tiparelor

Acest operator este simbolizat prin : şi permite efectuarea de teste direct asupra
câmpurilor tiparelor. Operatorul : trebuie să fie urmat de un predicat CLIPS şi efectul său este
ilustrat prin exemplul următor. Fie următoarea regulă:

(defrule R14
(numar ?nr)
(test (> ?nr 100) )
=>
(printout t ?nr “ satisface restrictia impusa ” crlf) )

Conform celor precizate în secţiunea anterioară, această regulă va fi activată dacă în BF există un
fapt care are pe prima poziţie simbolul numar, iar pe a doua poziţie un număr strict mai mare ca
100. Cu ajutorul operatorului de restricţie asupra câmpurilor tiparelor, regula poate fi rescrisă
într-o formă cu un singur tipar:

(defrule R15
(numar ?nr&:(> ?nr 100) )
=>
(printout t ?nr “ satisface restrictia impusa ” crlf)
)

În regula scrisă în acest fel operatorul & are rolul de a asigura legarea variabilei ?nr la valoarea
celui de-al doilea câmp din faptul ce satisface tiparul regulii (conform secţiunii 1 a referatului),
iar prin folosirea operatorului : se cere şi verificarea unei condiţii asupra valorii variabilei
respective, condiţie determinată aici de predicatul >. Deci regula R15 este activată numai dacă

47
predicatul care urmează după : determină valoarea adevărat, ceea ce înseamnă că regula R15 este
echivalentă cu regula R14. Despre succesiunea &: se spune că are înţelesul lui “astfel încât”.

Observaţii
1. Operatorul : trebuie să fie întotdeauna urmat de un predicat, adică de o funcţie având
drept rezultat o valoare de adevăr.
2. Din punct de vedere al eficienţei (timp de execuţie şi memorie ocupată), regula R15 este
mai eficientă decât R14; ea are un singur tipar, în comparaţie cu prima variantă, care are două
tipare - cel obişnuit şi cel de test. Deci, pentru eficienţă, vor fi de preferat testele efectuate direct
asupra câmpurilor tiparelor, faţă de cele efectuate într-un tipar suplimentar de test.
3. Una din utilizările obişnuite pentru operatorul : este aceea a verificării corectitudinii
datelor de intrare. În CLIPS datele de intrare sunt sub formă de fapte şi verificarea va însemna
efectuarea unor teste asupra câmpurilor din fapte. Un exemplu ilustrativ este următorul.

Exemplu
Să se scrie o regulă care să fie activată de fapte cu trei câmpuri. Primul câmp trebuie să fie
simbolul data, al doilea câmp trebuie să fie un număr întreg, iar al treilea câmp trebuie să fie un
şir de caractere sau un simbol. Vom da mai multe variante ale acestei reguli.

(defrule R16
(data ?n&:(integerp ?n) ?x&:(stringp ?x) |: (symbolp ?x))
=>
(printout t “data ” ?n “ este ” ?x crlf) )

În această variantă am realizat operaţia SAU între cele două condiţii privind tipul admisibil
pentru ultimul câmp cu ajutorul operatorului | . Deoarece în cazul regulii de mai sus acest
operator nu este urmat de o constantă (ca în utilizările din secţiunea 1, de exemplu regula R4), el
a fost succedat de operatorul : care poate fi urmat de un predicat.

(defrule R17
(data ?n&:(integerp ?n) ?x&:(or (stringp ?x) (symbolp ?x)) )
=>
(printout t “data ” ?n “ este ” ?x crlf) )

Aici operaţia SAU a fost realizată cu predicatul “or”.

(defrule R18
(data ?n&:(integerp ?n) ?x&:(not (numberp ?x) ) )
=>
(printout t “data ” ?n “ este ” ?x crlf) )

Aici am ţinut seama de faptul că există numai trei tipuri de constante (date primitive în CLIPS):
simboluri, şiruri de caractere şi numere. În consecinţă, se poate rescrie condiţia ca un câmp să fie
de tip şir de caractere sau simbol sub forma condiţiei ca acel câmp să nu fie număr.

(defrule R19
(data ?n&:(integerp ?n) ?x&~: (numberp ?x) )
=>
(printout t “data ” ?n “ este ” ?x crlf) )

48
Schimbarea faţă de varianta anterioară este aceea că am folosit operatorul de negare asupra
câmpurilor tiparelor, adică ~. Deoarece ~ nu poate fi urmat de un predicat a fost necesar să
folosim operatorul : în faţa predicatului.

(defrule R20
(data ?n ?x )
(test (and (integerp ?n)
(or (stringp ?x) (symbolp ?x) )
)
)
=>
(printout t “data ” ?n “ este ” ?x crlf) )

Această ultimă variantă se bazează pe folosirea unui tipar de test, în care se verifică toate
condiţiile pe care trebuie să le îndeplinească valorile variabilelor ?n şi ?x. Ar mai putea fi scrisă
o variantă, de acelaşi fel cu ultima, în care în tiparul de test, în loc de predicatul “or” să apară
(not (numberp ?x)).
Din punct de vedere al eficienţei variantele R16 – R19 sunt echivalente şi mai bune decât
R20, care are un tipar în plus, cel de test.

Exerciţiu
Rezolvaţi cele două exerciţii de la sfârşitul secţiunii 2, rescriind regulile respective în aşa
fel încât acestea să nu mai folosească tipare de test.

4. Relaţia = în CLIPS

Există trei utilizări distincte ale lui = în CLIPS, după cum urmează.

a) Se poate utiliza = în partea de acţiune a unei reguli, în interiorul unei comenzi “assert”,
având rolul unui operator de atribuire. Se va folosi = în faţa unei expresii, determinând atribuirea
câmpului respectiv din faptul care se introduce prin “assert”, cu valoarea la care se evaluează
expresia în faţa căreia se află =. În fapt, începând cu CLIPS versiunea 6 operatorul = se poate
omite în partea de acţiune a unei reguli, astfel că atât regula R21 cât şi R22 date mai jos sunt
corecte şi produc acelaşi rezultat.

Exemplu

(defrule R21
(fapt1 ?n1&:(numberp ?n1))
(fapt2 ?n2&:(numberp ?n2))
=>
(assert (fapt-suma = (+ ?n1 ?n2)) ) )

(defrule R22
(fapt1 ?n1&:(numberp ?n1))
(fapt2 ?n2&:(numberp ?n2))
=>
(assert (fapt-suma (+ ?n1 ?n2)) ) )

Conform părţii de acţiune, regulile R21 şi R22 determină introducerea în BF a unui fapt având pe
prima poziţie simbolul fapt-suma, iar pe a doua poziţie numărul rezultat din sumarea
valorilor la care s-au legat variabilele ?n1 şi ?n2 în procesul de potrivire.

49
b) Se poate utiliza = în partea de condiţie a unei reguli, în interiorul unui tipar în faţa unei
expresii, expresie prin a cărei evaluare trebuie să se obţină o constantă. În acest fel, cerinţa de
potrivire este ca în faptul care va satisface tiparul, pe poziţia respectivă să fie constanta rezultată
prin evaluarea expresiei în faţă căreia se află =.

Exemplu

(defrule R23
(a ?x =(+ ?x 10) )
(b =(* ?x 5))
=>
(printout t ?x “Satisface restricţiile cerute ” crlf))

Această regulă va fi activată dacă în BF se găseşte o pereche de fapte care să satisfacă


următoarele condiţii: primul fapt trebuie să aibă 3 câmpuri - primul trebuie să fie a, al doilea
câmp poate fi orice număr, iar al treilea câmp trebuie să fie un număr cu 10 mai mare decât cel
aflat pe poziţia a doua; al doilea fapt trebuie să aibă pe prima poziţie b, iar pe poziţia a doua
trebuie să fie un număr de 5 ori mai mare decât câmpul doi din primul fapt (de 5 ori valoarea
variabilei ?x). De exemplu, perechea de fapte (a 2 12) (b 10) va determina activarea regulii
R23.

Exerciţiu
Experimentaţi funcţionarea regulii R23, introducând în BF faptele:
(a 2 12) (b 10) (a abc 3).

Explicaţia comportării obţinute este următoarea. Atunci când în BF se introduce


faptul (a abc 3) interpretorul CLIPS va încerca potrivirea faptului pe primul tipar al regulii
R23 şi va semnala eroare, deoarece funcţia + apelată în determinarea restricţiei pentru câmpul
trei nu va putea aduna simbolul abc. De aceea, este de recomandat ca în locul regulii R23 să
folosim regula R24:

(defrule R24
(a ?x&:(numberp ?x) =(+ ?x 10) )
(b =(* ?x 5))
=>
(printout t ?x “Satisface restricţiile cerute ” crlf))

Observaţii
1. Operatorul = se poate folosi împreună cu operatorii logici în crearea condiţiilor de
potrivire în partea stângă a regulilor. Un exemplu este următorul:

(defrule R25
(a ?x ~=(/ ?x 10))
(b ?y&=(+ 5 ?x)|=(- 12 ?x)) => )

Această regulă va fi activată, de exemplu, de perechea de fapte: (a 40 5)(b -28). (Explicaţi


de ce).
2. Atunci când în expresia în faţa căreia se află = apare o variabilă, aceasta trebuie să fie o
variabilă deja legată. O regulă greşită din acest punct de vedere este următoarea:

(defrule R26
(a =(+ ?x 3))

50
(b ?x)
=>
)

în care variabila ?x folosită în expresia de sumare din primul tipar nu este legată (interpretorul va
semnala eroare). Dacă se inversează ordinea tiparelor regula devine corectă:

(defrule R27
(b ?x)
(a =(+ ?x 3))
=> )

Exerciţiu
Precizaţi o pereche de fapte care activează regula de mai sus.

c) Cea de-a treia utilizare a lui = este aceea deja precizată în secţiunea 2, de predicat de
testare a egalităţii a două valori numerice; această utilizare este ilustrată în exemplul următor.

(defrule R28
(dreptunghi ?identificator lungime ?x)
(dreptunghi ?identificator latime ?y)
(test (or (= ?y (+ 8 ?x))
(= ?y (- 20 ?x)) ) )
=>
(printout t “S-a gasit un dreptunghi cu identificatorul ” ?identificator crlf
“ care satisface conditiile cerute: lungime = ” ?x “ latime = ” ?y crlf) )

Dacă în BF sunt informaţii cu privire la dimensiunile unor dreptunghiuri, în sensul că


pentru fiecare dreptunghi există două fapte, unul precizând mărimea lungimii şi altul a lăţimii,
atunci regula de mai sus determină toate dreptunghiurile pentru care mărimea lăţimii este cu 8
mai mare ca lungimea, sau egală cu 20 - lungimea. De exemplu, faptele (dreptunghi 1 lungime
15) şi (dreptunghi 1 latime 5) vor activa regula. În această regulă = a fost folosit ca predicat
de testare a egalităţii a două valori numerice.
Alte trei variante echivalente de scriere a regulii de mai sus sunt prezentate în continuare.

(defrule R29
(dreptunghi ?identificator lungime ?x)
(dreptunghi ?identificator latime ?y&: (= ?y (+ 8 ?x)) |: ( = ?y (- 20 ?x)) )
=>
(printout t “S-a gasit un dreptunghi cu identificatorul ” ?identificator crlf
“ care satisface conditiile cerute: lungime = ” ?x “ latime = ” ?y crlf) )

Aici = s-a folosit tot ca predicat, dar nu într-un tipar de test ca în varianta anterioară, ci într-un
tipar obişnuit, determinând o restricţie asupra variabilei ?y, împreună cu operatorul : . O variantă
apropiată de aceasta este:

(defrule R30
(dreptunghi ?identificator lungime ?x)
(dreptunghi ?identificator latime ?y&: (or (= ?y (+ 8 ?x) ) ( = ?y (- 20 ?x)) ) )
=>
(printout t “S-a gasit un dreptunghi cu identificatorul ” ?identificator crlf
“ care satisface conditiile cerute: lungime = ” ?x “ latime = ” ?y crlf) )

51
în care deosebirea faţă de varianta anterioară este aceea că operaţia SAU a fost realizată cu
predicatul “or” în locul operatorului |.

(defrule R31
(dreptunghi ?identificator lungime ?x)
(dreptunghi ?identificator latime ?y& = (+ 8 ?x) | = (- 20 ?x))
=>
(printout t “S-a gasit un dreptunghi cu identificatorul ” ?identificator crlf
“ care satisface conditiile cerute: lungime = ” ?x “ latime = ” ?y crlf) )

În această variantă = s-a folosit în sensul de la punctul b), în faţa unei expresii, împreună cu
operatorul &, care asigură legarea variabilei ?y şi cu operatorul | pentru realizarea operaţiei SAU.

Observaţii în legătură cu realizarea testelor în interiorul tiparelor


Pe baza celor prezentate până aici se pot formula următoarele observaţii.

1. Deşi tiparele de test sunt mai uşor de scris, din punct de vedere al eficienţei va fi bine să
efectuăm testele în interiorul tiparelor, fără a adăuga tiparul suplimentar de test (de exemplu,
regula R29 este mai eficientă decât R28).

2. După operatorul de negare ~ poate să urmeze o constantă (simbol, şir de caractere sau
număr). În acest caz semnificaţia este aceea că se cere o potrivire care să fie diferită de câmpul
care urmează după operatorul de negare, ca de exemplu în regula R3. După ~ poate să urmeze şi
o variabilă, care trebuie să fie deja legată la momentul respectiv, semnificaţia fiind aceea că se
cere o potrivire diferită de valoarea variabilei. Un exemplu în acest sens, este regula R8, în care
se cere ca acel câmp care se va lega la ?y să fie diferit de valoarea variabilei ?x, ce este deja
legată, din potrivirea pentru primul tipar al regulii R8. De asemenea, după ~ poate să urmeze :,
ca în regula R19, sau =, ca în regula R25. De subliniat că operatorul de negare ~ realizează
numai negarea unui singur operand (următorul operand), adică ~ nu poate nega o expresie logică
de tip compus. Un exemplu în acest sens este următorul.

(defrule R32
(data ?x&~:(stringp ?x) |: (eq ?x “abc”) )
=> (printout t “Data ” ?x “ satisface restrictiile ” crlf) )

Regula R32 este activată de un fapt care are pe prima poziţie simbolul data, iar pe a doua poziţie
trebuie să nu fie un şir de caractere, sau să fie şirul de caractere “abc”. Dacă dorim scrierea unei
reguli care să fie activată de un fapt ce are ca prim câmp simbolul data, iar al doilea câmp să nu
fie de tip şir de caractere şi nici să nu fie egal cu simbolul abc, atunci regula este:

(defrule R33
(data ?x&:(not (or (stringp ?x) (eq ?x abc) )) )
=> (printout t “Data ” ?x “ satisface restrictiile. ” crlf) )

Exerciţiu
Dacă în regula de mai sus, condiţia “or” devine “and” se ajunge la regula:
(defrule R34
(data ?x&:(not (and (stringp ?x) (eq ?x abc) )) )
=> (printout t “Data ” ?x “ satisface restrictiile ” crlf) )

52
Explicaţi când este activată această regulă şi respectiv când este activată regula:

(defrule R35
(data ?x&:(not (and (stringp ?x) (eq ?x “abc”) )) )
=> (printout t “Data ” ?x “ satisface restrictiile ” crlf) )

Care sunt formele echivalente, mai simple, pentru regulile R34 şi R35 ?

3. După operatorii | şi & trebuie să urmeze o constantă, o variabilă, :, = , sau ~ (vezi


regulile scrise în secţiunile anterioare).

4. Variabilele care se folosesc în condiţii compuse în tiparele regulilor trebuie manevrate


cu atenţie, respectând următoarele condiţii:
• dacă variabila este pe prima poziţie a condiţiei, atunci va fi conectată de restul condiţiei
prin operatorul de legare a variabilelor & (ca variabila ?x în regula R33), sau prin operatorul |,
caz în care variabila trebuie să fie deja legată (un exemplu în acest sens este tiparul al doilea din
regula R10; acesta este: (student ?nume2&~?nume1 AC ? ?gr ?dom | Bucuresti) şi aici
variabila ?dom este conectată cu operatorul |, ea fiind deja legată din potrivirea pentru primul
tipar al regulii R10).
• dacă variabila nu apare pe prima poziţie a condiţiei, atunci ea trebuie să fie deja legată
(aşa cum este, de exemplu, variabila ?nume1 în acelaşi tipar de mai sus al regulii R10).

Exerciţiu
Într-o BF se găsesc informaţii cu privire la dimensiunile unor dreptunghiuri, în fapte de
forma: (dreptunghi <lungime> <latime>). Scrieţi programul CLIPS care să determine suma
ariilor dreptunghiurilor care au lungimea şi laţimea mai mari decât 5, pentru care avem
informaţii în BF, valoarea respectivă urmând a fi păstrată într-un fapt de forma: (suma-ariilor
<valoare>).

5. Operaţiile logice între tiparele unei reguli

Pe lângă operaţiile logice între câmpurile tiparelor, prezentate până aici, CLIPS admite şi
efectuarea de operaţii logice între tiparele unei reguli. Aşa cum se ştie, în mod implicit între
tiparele unei reguli se execută operaţia ŞI, regula fiind activată numai atunci când toate tiparele
ei sunt verificate de fapte din BF. În plus, CLIPS permite şi folosirea unor operaţii logice
explicite între tiparele unei reguli.

Operaţia SAU între tiparele unei reguli


Plecăm de la următorul exemplu; presupunem că în BR există regulile:

(defrule R36
(alarma apa)
=> (printout t “Deconecteaza electricitatea” crlf) )

(defrule R37
(alarma incendiu)
=> (printout t “Deconecteaza electricitatea” crlf) )

(defrule R38
(alarma explozie)
=> (printout t “Deconecteaza electricitatea” crlf) )

53
Se observă cum cele trei reguli au aceeaşi parte de acţiune. În locul scrierii acestor reguli,
se poate folosi una singură având operaţia SAU între tipare:

(defrule R39
(or (alarma apa) (alarma incendiu) (alarma explozie) )
=> (printout t “Deconecteaza electricitatea” crlf) )

Observaţii
1. Din acest exemplu, rezultă sintaxa operaţiei SAU între tiparele unei reguli:

(or <<<tipar>>>)

unde tipar poate fi un tipar simplu, sau unul compus prin folosirea operaţiilor logice între tipare;
de asemenea, sub operaţia OR se poate şi prelua adresa unui fapt ce satisface un tipar, ca într-un
exemplu care va urma (vezi regula R42).
2. Regula R39 trebuie înţeleasă în sensul existenţei a trei reguli distincte în BR. Astfel,
dacă în BF există concomitent trei fapte care să satisfacă tiparele de sub condiţia OR, regula R39
va fi activată de trei ori, câte o dată pentru fiecare fapt.

Exerciţiu
Experimentaţi aspectul menţionat în observaţia de mai sus, introducând regula R39 în BR
şi punând în BF cele trei fapte care să satisfacă tiparele de sub condiţia SAU.

3. Într-o regulă, în afara operaţiei SAU, pot fi incluse şi alte tipare, legătura fiind asigurată
implicit de operaţia ŞI, ca în următorul exemplu.

(defrule R40
(tensiune on)
(or (alarma apa) (alarma incendiu) (alarma explozie) )
=> (printout t “Deconecteaza electricitatea” crlf)
)

Din nou trebuie înţeles că această regulă este echivalentă cu trei reguli distincte.

Exerciţiu
Precizaţi care sunt cele trei reguli echivalente cu R40.

4. Activarea şi apoi execuţia succesivă a unei reguli ce conţine o operaţie OR între tipare,
de mai multe ori (datorită satisfacerii mai multor tipare de sub OR) poate fi supărătoare
(nedorită). Acest inconvenient poate fi eliminat, ca în exemplul următor.

(defrule R41
?x <- (tensiune on)
(or (alarma apa) (alarma incendiu) (alarma explozie) )
=>
(retract ?x)
(assert (tensiune off))
(printout t “Deconecteaza electricitatea” crlf) )

În acest caz regula va fi executată doar o dată, chiar dacă în BF există faptele care să
satisfacă toate cele trei tipare de sub OR, deoarece ştergerea faptului (tensiune on) în partea de
acţiune a regulii R41 determină ca după prima execuţie regula să nu mai fie satisfăcută.

54
Dacă se pune problema să rescriem regula R41 în aşa fel încât, în partea de acţiune ea să
determine şi ştergerea din BF a faptului care a determinat satisfacerea condiţiei OR, varianta
corectă este:

(defrule R42
?x <- (tensiune on)
(or
?motiv <- (alarma apa)
?motiv <- (alarma incendiu)
?motiv <- (alarma explozie) )
=>
(retract ?x ?motiv)
(assert (tensiune off))
(printout t “Deconecteaza electricitatea” crlf) )

Se remarcă felul în care pentru adresele faptelor care satisfac tiparele de sub condiţia OR s-
a folosit o variabilă unică: ?motiv. La prima vedere aceasta poate părea incorect, dar trebuie să
ţinem seama că regula de mai sus este echivalenta a trei reguli distincte. Dacă s-ar fi folosit trei
variabile diferite nu s-ar fi putut scrie în mod corect acţiunea “retract”, adică varianta următoare
este greşită:

(defrule R43
?x <- (tensiune on)
(or
?motiv1 <- (alarma apa)
?motiv2 <- (alarma incendiu)
?motiv3 <- (alarma explozie) )
=>
(retract ?x ?motiv1 ?motiv2 ?motiv3)
(assert (tensiune off))
(printout t “Deconecteaza electricitatea” crlf) )

Eroarea este aceea că dacă, de exemplu, regula este activată de faptele (tensiune on) şi
(alarma apa), în partea de acţiune a regulii două din cele trei variabile “motiv” nu vor fi legate
(variabilele ?motiv2 şi ?motiv3 nu sunt legate).

Operaţia ŞI între tiparele unei reguli


Datorită existenţei implicite a operaţiei ŞI între tipare, această condiţie nu apare ca
necesară decât pentru materializarea unor expresii logice complexe între tipare, ca în exemplul
următor.

(defrule R44
?x <- (tensiune on)
(or
(alarma apa)
(and
(alarma foc)
(incendiu tip_C) )
(alarma explozie) )
=>
(retract ?x)
(assert (tensiune off))

55
(printout t “Deconecteaza electricitatea” crlf) )

Observaţii
1. Datorită folosirii condiţiei OR, această regulă este echivalentă cu trei reguli.

Exerciţii
a. Scrieţi cele trei reguli echivalente cu R44.
b. Cum se poate obţine ca în exemplul cu regula R44 să se realizeze pe lângă ştergerea din
BF a faptului (tensiune on) şi ştergerea celorlalte fapte care au determinat execuţia regulii?

2. Fără a avea nici un avantaj, în loc de

(defrule R45
(a)
(b) =>
)

putem scrie:

(defrule R46
(and (a)
(b) ) => )

Operaţia de negaţie asupra unui tipar


Această operaţie permite scrierea regulilor care să fie activate atunci când un anume fapt
lipseşte din BF. De exemplu, fie regulile:

(defrule R47
(faza informare-operator)
(alarma ?tip)
=>
(printout t “Exista in sistem alarma ” ?tip crlf) )

(defrule R48
(faza informare-operator)
(not (alarma ?) )
=>
(printout t “Nu exista in sistem nici o alarma ” crlf) )

Dacă BF conţine, de exemplu, faptul (faza informare-operator) şi faptul (alarma foc),


atunci regula R47 este activată, iar R48 nu este activată. Aceasta din urmă va fi activată atunci
când în BF există faptul (faza informare-operator), şi în plus, datorită tiparului (not (alarma ?)
), va fi activată numai dacă în BF nu există nici un fapt cu două câmpuri, având primul câmp
simbolul alarma. În acest fel, regulile R47 şi R48 sunt mutul exclusive (nu pot fi prezente
simultan în agendă). În concluzie, sintaxa operaţiei de negare asupra unui tipar este:

(not <tipar-simplu sau tipar-compus>)

unde tipar-compus este unul dezvoltat cu ajutorul operaţiilor OR, AND.

Exemplu
Regula care determină cel mai mare număr dintr-o colecţie de fapte de forma:

56
(numar <valoare-numerică>) este:

(defrule R49
(numar ?x)
(not (numar ?y&:(> ?y ?x)))
=>
(printout t “Maximum = ” ?x crlf)
)

Observaţii
1. Este important locul plasării unei operaţii “not” în şirul de tipare din partea stângă a unei
reguli. De exemplu, fie următoarele două reguli:

(defrule R50
(pereche ?x ?y)
(pereche ?y ?z)
(not (triplet ?x ?x ?z))
=>)

(defrule R51
(not (triplet ?x ?x ?z))
(pereche ?x ?y)
(pereche ?y ?z)
=>)

şi fie în BF următoarele fapte:

f-0 (pereche 3 4)
f-1 (pereche 4 5)
f-2 (triplet 4 4 6)

Vom constata că regula R50 este activată, iar R51 nu. Explicaţia este următoarea. Pentru regula
R50 faptul de la identificatorul f - 0 satisface primul tipar, cu legarea variabilei ?x la valoarea 3
şi a variabilei ?y la valoarea 4. Atunci, faptul de la identificatorul f - 1 satisface al doilea tipar, cu
legarea variabilei ?z la valaorea 5. Cu această legare a variabilelor, cum în BF nu există faptul
(triplet 3 3 5) şi al treilea tipar al lui R50 (cel de tip “not”) este satisfăcut şi regula este
activată. În schimb, regula R51 nu este activată deoarece faptul de la identificatorul f - 2
determină nesatisfacerea primului tipar al acestei reguli (acesta cere ca în BF să nu existe un fapt
de forma “triplet” în care câmpurile doi şi trei să fie egale). O concluzie care se desprinde din
acest exemplu este şi aceea că tiparele unei reguli, în procesul de potrivire, sunt considerate în
ordinea în care apar în partea stângă a regulii.

2. Atunci când o condiţie “not” este satisfăcută, nu se produce o legare a variabilelor de


sub condiţia “not”. De exemplu, fie următoarea regulă:

(defrule R52
(not (data ?x))
=>
(printout t ?x crlf)
)

57
Interpretorul CLIPS va semnala eroare la introducerea acestei reguli deoarece variabila ?x, fiind
sub tiparul căruia i se aplică operaţia de negaţie, rămâne nelegată.

Exerciţiu
Precizaţi care dintre următoarele reguli este corectă şi care nu. Explicaţi de ce.

(defrule R53 (not (fapt ?x)) (test (> ?x 4)) => )


(defrule R54 (not (fapt ?x&:(> ?x 4))) => )
(defrule R55 (not (fapt ?x)) (fapt ?y&:(> ?y ?x)) => )
(defrule R56 (not (fapt ?x)) (fapt ?x&:(> ?x 4)) => )

3. Condiţia “not” poate fi folosită şi pentru negarea unui tipar compus. De exemplu, regula
următoare este corectă:

(defrule R57
(not (or (a) (b) ) )
=> )

Exerciţii
1. Explicaţi când se activează regula R57
2. Explicaţi când se activează regula R58:

(defrule R58
(not (and (a ?x) (b ?x) ) )
=>
(assert (c) )
)

Trebuie înţeles că atunci când o condiţie NOT este folosită (o operaţie de negaţie asupra
unui tipar), motorul de inferenţe va cerceta întreg conţinutul BF; din acest punct de vedere putem
asemăna o condiţie NOT cu o buclă ”while” sau ”for” din programarea procedurală. Pentru a
înţelege funcţionarea condiţiei NOT sunt ilustrative şi următoarele două reguli:

(defrule R59
(data S1 100)
(not (tip ?v&:(< ?v 0)) )
=>
(printout t “Faptul data este prezent si niciun fapt tip cu o valoare negativa nu
este prezent” crlf)
)

(defrule R60
(data S1 100)
(tip ?v&:(not (< ?v 0) ))
=>
(printout t “Faptul data este prezent si cel putin un fapt tip cu o valoare pozitiva
este prezent” crlf)
)

Considerăm următoarele situaţii distincte privind conţinutul BF:

58
BF1:

f-1 (data S1 100)


f-2 (tip 12)
f-3 (tip 13)

BF2:
f-1 (data S1 100)

BF3:
f-1 (data S1 100)
f-2 (tip 12)
f-3 (tip 13)
f-4 (tip -10)

BF4:
f-1 (data S1 100)
f-2 (tip -12)

Felul în care se activează regulile R59 şi R60 în cele patru cazuri este următorul. Pentru
BF1 ambele reguli sunt activate – regula R59 o dată, iar R60 de două ori, câte o dată pentru
faptele de la adresele f-2 şi f-3 potrivite pe al doilea tipar al regulii. Dacă suntem în cazul BF2,
atunci numai regula R59 este activată; R60 nu este activată neexistând niciun fapt care să
satisfacă al doilea tipar al regulii. În cazul situaţiei date de BF3 numai R60 este activată, de două
ori, corespunzător existenţei faptelor de la adresele f-2 şi f-3; în acest caz regula R59 nu este
activată datorită existenţei faptului de la adresa f-4, fapt care determină nesatisfacerea condiţiei
NOT. În cazul lui BF4 niciuna din cele două reguli nu este activată. Se poate concluziona asupra
condiţiilor logice pe care le verifică aceste două reguli privind faptele care încep cu simbolul tip:
regula R59 este activată numai atunci când niciun fapt de două câmpuri, cu primul câmp fiind
simbolul tip şi al doilea câmp fiind un număr negativ nu este prezent în BF, în timp ce regula
R60 este activată atunci când cel puţin un fapt cu primul câmp simbolul tip şi al doilea câmp o
valoare pozitivă există în BF.

Operaţiile exists şi forall asupra tiparelor

Aceşti operatori determină pentru limbajul CLIPS posibilităţi de testare apropiate de cele
pe care în logică le putem obţine cu ajutorul cuantificatorilor („există”, „oricare”).
Condiţia de existenţă a unui tipar sau a unor tipare satisfăcute cel puţin o dată de faptele
din BF se obţine prin utilizarea operaţiei ”exists” asupra unui tipar sau unor tipare. Altfel spus, se
testează dacă există cel puţin un set de fapte în BF care satisfac condiţia cerută de tiparele puse
în interiorul condiţiei ”exists”. Sintaxa operaţiei este:

(exists <<<tipar >>>)

De exemplu fie următoarele două reguli:

(defrule R61
(exists (data ?x)
(data1 ?x) )
=> (printout t Succes crlf) )

59
(defrule R62
(data ?x)
(data1 ?x)
=> (printout t ?x crlf) )

Dacă BF este:

f-1 (data 100)


f-2 (data1 100)
f-3 (data 200)
f-4 (data1 200)
f-5 (data 300)
f-6 (data1 300)
f-7 (data 400)

atunci regula R61 va fi plasată în agendă o singură dată, în timp ce regula R62 va fi plasată de
trei ori. Operaţia ”exists” este în fapt materializată în CLIPS pe baza operaţiei de negare asupra
unui tipar (condiţia NOT) şi se supune restricţiilor acesteia. Aceasta înseamnă că o variabilă
folosită în interiorul unei condiţii ”exists” nu va fi legată (atribuită), ci doar poate fi folosită
pentru a crea anumite restricţii ce trebuie satisfăcute; în consecinţă o asemenea variabilă nu va
putea să apară în partea de acţiune a regulii (a se observa diferenţa dintre părţile de acţiune ale
regulilor R61 şi R62).

Operatorul ”forall” asigură un mecanism pentru a verifica dacă un grup de tipare este
satisfăcut pentru fiecare (orice) apariţie a unui alt tipar specificat. Sintaxa pentru acest operator
este:

(forall <tipar_1> <<<tipar_i>>>)

Condiţia este satisfăcută dacă pentru fiecare fapt ce verifică ”tipar_1”, toate condiţiile
determinate de grupul de tipare ”tipar_i” sunt, de asemenea, satisfăcute. Ca un exemplu,
considerăm că în BF există fapte care respectă următoarele două şabloane:

(Student <nume>)
(Nota <proba> <nume> <nota>)

În aceste condiţii regula de mai jos este activată doar atunci când în BF pentru toate faptele
de forma ”Student” faptele corespunzătoare cu note conţin valoarea notei strict mai mare decât 4.

(defrule R63
(forall (Student ?nume)
(Nota Laborator ?nume ?x1&:(> ?x1 4))
(Nota Examen ?nume ?x2&:(> ?x2 4))
(Nota Test ?nume ?x3&:(> ?x3 4)) )
=>
(printout t “Student promovat” crlf) )

Dacă BF conţine faptele: (Student Ion), (Nota Laborator Ion 5), (Nota Examen Ion
7), (Nota Test Ion 10), atunci regula este activată. Dacă pe lângă aceste fapte, în BF apar şi
faptele (Student Vasile) (Nota Laborator Vasile 5), atunci regula nu mai este activată,
deoarece pentru faptul corespunzător studentului Vasile nu există în BF faptele care să precizeze
nota la Examen şi nota la Test. Un alt caz de ne-activare a regulii este acela când în BF ar fi

60
faptele: (Student Ion1), (Nota Laborator Ion1 6), (Nota Examen Ion1 3), (Nota Test
Ion1 10), de data aceasta nefiind satisfăcută condiţia cerută pentru nota la examen. Este de
menţionat că dacă în BF nu există niciun fapt Student (niciun fapt care să satisfacă acea condiţie
din primul tipar ce apare în ”forall”, atunci regula este activată.
Operaţia ”forall”, la fel ca ”exists”, este materializată pe baza operaţiei de negare asupra
tiparelor şi se supune restricţiilor acesteia. Operaţiile ”forall” şi ”exists” pot fi utilizate şi
împreună cu condiţii obişnuite, ca în exemplul de mai jos:

(defrule R64
(Fapt ?y)
(exists (data ?y&:(< ?y 0))
(data1 ?y ?) )
=>
(printout t ?y crlf) )

Regula de mai sus este plasată de două ori în agendă dacă în BF sunt faptele: (Fapt -12),
(Fapt aa), (data -12), (data1 -12 as), (data1 -12 ae), (data -3), (data1 -3 v), (Fapt -3).

Exerciţiu

Creaţi câte un exemplu în care să verificaţi funcţionarea condiţiilor ”exists” şi ”forall”.

Exerciţii recapitulative

1. Răspundeţi la următoarele întrebări.


• Care sunt operaţiile logice care se pot efectua asupra câmpurilor tiparelor şi respectiv
între tipare ?
• Precizaţi când sunt activate următoarele reguli:
(defrule R65 (data a1 | ~a2&~a3) => )
(defrule R66 (data ?x ~?x ?y ?x | ~?y) => )
• Precizaţi dacă următoarele reguli sunt corecte:
(defrule R67 (data ?x ~?y) => )
(defrule R68 (data ?x ?y ?x&?y) => )
(defrule R69 (data ?x ~a ?y&~a) => )
• Ce se înţelege prin predicat CLIPS?
• Ce se înţelege prin tipar de test în CLIPS?
• Când va fi activată regula:
(defrule R70 (data ?x ?y)
(test
(or (and (numberp ?x) (numberp ?y)
(oddp ?x) (evenp ?y) (= (/ ?x ?y) 0.5) )
(not (and (eq ?x a)(eq ?y b) ) ) ) ) => )
• Când trebuie folosit operatorul de restricţie asupra câmpurilor tiparelor : ?
• Rescrieţi regula R70 în aşa fel încât să nu mai apară tiparul de test.
• Care sunt cele trei utilizări ale lui = în CLIPS ?
• Când este activată regula următoare:
(defrule R71
(data ?x&:(integerp ?x) ?y&:(numberp ?y) )
(data ?z&=(+ ?x 1) =(+ ?z 1)|=(- ?y 1) ) => )
• Este corectă regula:
(defrule R72 (data ?x =(+ ?x ?y) ?y) => )
• Când este necesară folosirea operaţiei SAU între tiparele unei reguli ?

61
• Când este necesară folosirea operaţiei ŞI între tiparele unei reguli ?
• Când este necesară folosirea operaţiei de negaţie asupra unui tipar al unei reguli ?

2. Scrieţi regulile echivalente regulilor R61 şi R63 folosind operaţia de negare asupra
tiparelor.

3. Într-o BF se găsesc informaţii cu privire la dimensiunile unor figuri geometrice plane


(triunghiuri, pătrate, dreptunghiuri, etc.). Scrieţi programul CLIPS care să determine suma
perimetrelor figurilor geometrice asupra cărora există informaţii în BF.

4. Scrieţi programul CLIPS (se vor concepe faptele şi regulile necesare) care să determine:
a) intersecţia a două mulţimi;
b) reuniunea a două mulţimi;
c) diferenţa a două mulţimi.

62
Lucrarea nr. 6

Tehnici de intrare şi de ieşire în CLIPS; elemente procedurale şi comenzi


utilitare
1. Lucrul cu fişiere în CLIPS

În CLIPS se pot utiliza fişiere care trebuie în prealabil deschise, folosind funcţia:

(open <nume-fişier> <nume-logic> [<acces>])

nume-fişier – trebuie să fie de tip simbol sau şir de caractere şi va reprezenta numele sub
care este cunoscut fişierul respectiv de către sistemul de operare;
nume-logic – trebuie să fie o constantă CLIPS (simbol, şir de caractere sau număr) şi va
reprezenta numele sub care interpretorul CLIPS recunoaşte fişierul în cauză; acesta este
identificatorul pe care îl vom folosi în programele CLIPS atunci când ne referim la un fişier (este
numele care va fi utilizat, de exemplu, în reguli, în partea de acţiune, sau în comenzi date la nivel
superficial, cu privire la un fişier). Un nume-logic asignat nu poate fi folosit de mai multe ori
pentru diferite fişiere. Un avantaj al folosirii celor două argumente – nume-fişier, pentru sistemul
de operare şi nume-logic, pentru interpretorul CLIPS, este acela că putem uşor înlocui un fişier
cu altul într-un program, fără a trebui să facem schimbări majore în acesta.
acces – este un argument opţional şi fixează modul de acces la fişier; trebuie să fie de tip
şir de caractere, fiind posibile următoarele patru cazuri: “r” – fişier deschis numai pentru citire;
“w” - fişier deschis numai pentru scriere; “r+” - fişier deschis pentru citire şi scriere; “a” - fişier
deschis numai pentru concatenare (scriere la sfârşitul fişierului). Dacă argumentul acces lipseşte
se va da de către sistem valoarea implicită “r”.

Observaţii
1. Consecinţa operaţiei “open” depinde de sistemul de operare în care se lucrează. De
exemplu, în DOS sau în Windows, sisteme de operare care nu lucrează cu versiuni multiple ale
fişierelor, redeschiderea pentru scriere a unui fişier existent va determina înlocuirea acestuia cu
unul nou.
2. Funcţia “open” lucrează ca un predicat, în sensul că determină ca rezultat TRUE dacă
deschiderea fişierului s-a făcut cu succes şi FALSE în caz contrar. De exemplu, dacă se foloseşte
într-o comandă “open” numele unui fişier inexistent, cu argumentul acces egal cu “r” (deschidere
pentru citire), atunci rezultatul obţinut va fi FALSE: nu poate fi deschis pentru citire un fişier
care nu există. În acest fel, funcţia “open” poate fi inclusă în teste, care să transmită mesajele
corespunzătoare în cazul apariţiei unor erori.
Atunci când accesul la un fişier nu mai este necesar se va folosi funcţia “close”, pentru
închiderea acestuia. Dacă fişierul nu se închide nu avem garanţia că datele din el sunt salvate în
mod corect. De aceea, într-un program CLIPS, de îndată ce nu mai avem nevoie de un fişier, îl
vom închide cu funcţia “close”. Sintaxa pentru aceasta este:

(close [<nume-logic>])

unde argumentul nume-logic este opţional şi specifică numele logic (acelaşi cu cel declarat în
“open”) al fişierului care va fi închis. Dacă argumentul lipseşte CLIPS va închide toate fişierele
deschise. Funcţia “close” determină rezultatul TRUE dacă fişierul specificat a putut fi închis, sau
atunci când argumentul nume-logic lipseşte, rezultatul furnizat de “close” este TRUE dacă măcar
un fişier a putut fi închis; într-un caz contrar rezultatul lui “close” este FALSE.
Conform celor menţionate mai sus este bine să existe o regulă acţionând, dacă nu mai
devreme, în finalul rulării programului CLIPS şi care să închidă toate fişierele deschise în timpul

63
execuţiei acelui program. În legătură cu acest aspect, chiar interpretorul CLIPS va închide, la
execuţia comenzii “exit” de părăsire a interpretorului, toate fişierele rămase deschise.
Înscrierea şi citirea datelor din fişiere se poate face cu ajutorul funcţiilor “printout” şi
“read”. În programele anterioare “printout” a fost folosită numai pentru transmiterea de
informaţii pe terminal; în acest sens, reamintim forma generală a comenzii “printout”:

(printout <nume-logic> <<element-de-ieşire>>)

Dacă argumentul nume-logic este t, atunci elementele-de-ieşire sunt transmise pe terminal


(elementul standard de ieşire, monitor), iar dacă este numele-logic al unui fişier (ceea ce
înseamnă că fişierul a fost deschis cu o comandă “open” în care a apărut numele-logic respectiv),
atunci elementele-de-ieşire sunt înscrise în fişierul în cauză. De exemplu, în comanda:

(printout fdata 1 2 3 crlf)

numerele 1, 2 şi 3 sunt înscrise în fişierul având numele-logic fdata, pe o linie a fişierului


respectiv, după care se trece la o linie nouă. Aceasta presupune ca anterior execuţiei acestei
comenzi, să se fi executat, de exemplu, comanda:

(open f1.dat fdata “a”)

sau

(open f1.dat fdata “r+”)

sau

(open f1.dat fdata “w”)

Mai trebuie remarcat faptul că valorile respective sunt înscrise in fişier fără niciun spaţiu
între ele, astfel că la o citire care ar urma valoarea preluată din fişier va fi 123. Pentru a obţine
delimitarea prin spaţiu între valorile înscrise în fişier pe aceeaşi linie vom putea folosi un şir de
caractere care să conţină spaţiul necesar, adică vom avea o comandă de forma:

(printout fdata 1 ” ” 2 ” ” 3 ” ” crlf)

Pentru citirea unor informaţii dintr-un fişier se va folosi comanda:

(read [<nume-logic>])

Dacă argumentul opţional nume-logic este prezent atunci citirea se face din fişierul identificat
prin numele-logic respectiv, iar dacă argumentul lipseşte sau este simbolul t atunci informaţia
este preluată de la tastatură (elementul standard de intrare). Este de menţionat că “read” permite
introducerea unui singur câmp. De exemplu, fie următoarea succesiune de comenzi, date la nivel
superficial:

(read) ¶
abc 12¶
abc¶

În această succesiune, cea de-a treia linie, formată din simbolul abc, este răspunsul
interpretorului, care după comanda “read” preia de la tastatură numai primul câmp, astfel că

64
numărul 12, care este al doilea câmp introdus pe aceeaşi linie, se pierde. În mod similar, dacă se
execută comanda:

(read fintrari)

iar în fişierul având numele-logic fintrari pe linia curentă există mai multe câmpuri, numai
primul dintre ele este luat în considerare prin comanda de mai sus. Comanda “read” permite şi
preluarea unor câmpuri care nu sunt simboluri, şiruri de caractere sau numere, cum ar fi
parantezele; acestea vor fi tratate ca şiruri de caractere şi deci introduse între ghilimele.
Felul în care se poate lucra cu fişiere în CLIPS este ilustrat prin următorul exemplu.

Exemplu
Să se scrie programul CLIPS care să folosească două fişiere în felul următor. Din primul
fişier vor fi citite numere. Dacă numărul preluat este mai mare sau egal cu 60 atunci numărul
respectiv este transmis atât pe monitor cât şi în fişierul de ieşire, însoţit de cuvântul corect, iar
dacă numărul este strict mai mic ca 60, atunci este transmis pe monitor şi în fişierul de ieşire
însoţit de cuvântul incorect.
Fără să mai notăm forma faptelor pe care le vom folosi, expresiile acestora fiind simple,
vom scrie direct regulile necesare. Programul care rezolvă problema trebuie să aibă o desfăşurare
secvenţială, fiind organizat în următoarele faze: iniţializare, citire date din fişierul de intrare,
prelucrare date, transmitere date în fişierul de ieşire, terminare program. Dacă prima şi ultima
fază trebuie să se execute o singură dată, la începutul şi respectiv sfârşitul lucrului, celelalte trei
faze (citire, prelucrare, transmitere rezultate) trebuie să se execute în mod repetat, într-un ciclu
care să se încheie atunci când nu mai sunt date de citit din fişierul de intrare.
Faza de iniţializare are de rezolvat deschiderea fişierelor pe care le vom folosi în program,
ceea ce presupune folosirea următoarei reguli:

(defrule R1
=>
(open intrare.dat fdate)
(open iesire.txt fiesire "w")
(assert (faza citire) ) )

Deoarece această regulă trebuie să se execute prima, imediat după “reset”, partea ei de condiţie
este vidă. În partea de acţiune, pe lângă deschiderea celor două fişiere de lucru, se introduce în
BF faptul (faza citire) având rolul de a face trecerea la faza următoare a ciclului de lucru. Regula
care rezolvă citirea unei date din fişierul “intrare.dat” este:

(defrule R2
?a <- (faza citire)
=>
(retract ?a)
(assert (valoare-citita (read fdate))) )

Prin această regulă se introduce în BF un fapt cu primul câmp simbolul valoare-citita, iar al
doilea câmp valoarea preluată din fişierul având nume-logic fdate. Funcţia ”read” returnează
simbolul EOF atunci când printr-o operaţie de citire se ajunge la sfârşitul unui fişier. Astfel, la un
moment dat în BF vom avea faptul (valoare-citita EOF) care va marca terminarea fazei de citire
din fişier. În consecinţă putem scrie regula care va indica printr-un fapt în BF posibilitatea
începerii fazei de oprire (de finalizare a programului) şi respectiv regula prin care se rezolvă
terminarea programului, operaţia de efectuat fiind cea de închidere a fişierelor.

65
(defrule R3
"regula ce actioneaza atunci cand valoarea citita din fisier este EOF"
?a <-(valoare-citita EOF)
=>
(retract ?a)
(assert (faza oprire)))

(defrule R4
?a <- (faza oprire)
=>
(retract ?a) (close fdate) (close fiesire) )

În locul celor două comenzi ”close” din regula R4 am fi putut folosi una singură, fără nici un
argument - adică (close) - situaţie în care interpretorul CLIPS ar fi închis toate fişierele deschise
în acel moment. Cele două reguli (R3 şi R4), pot fi grupate într-o singură regulă, atunci când
faza de terminare citire reprezintă de fapt faza de terminare a programului.
Pentru rezolvarea fazei de prelucrare a datelor citite din fişier vom folosi trei reguli,
corespunzătoare celor trei situaţii posibile pentru valoarea citită: R5 pentru cazul când valoarea
preluată din fişier este număr şi este mai mare sau egal decât 60, R6 pentru o valoare preluată
care este număr şi este mai mică decât 60, respectiv R7 pentru cazul când valoarea preluată din
fişier nu este număr şi nu este nici simbolul EOF.

(defrule R5
?a <- (valoare-citita ?v&:(and (numberp ?v) (>= ?v 60) ))
=>
(retract ?a)
(printout t “S-a gasit o valoare corecta: ” ?v crlf)
(printout fiesire ?v “ corect” crlf)
(assert (faza citire) ) )

(defrule R6
?a <- (valoare-citita ?v&:(and (numberp ?v) (< ?v 60)))
=>
(retract ?a)
(printout t “S-a gasit o valoare incorecta: ” ?v crlf)
(printout fiesire ?v “ incorect” crlf)
(assert (faza citire) ) )

(defrule R7 “regula ce indeparteaza valorile nenumerice”


?a <- (valoare-citita ?v&:(not (or (numberp ?v) (eq ?v EOF))))
=>
(retract ?a)
(assert (faza citire) ) )

Este de observat că în programul astfel scris faptele (faza citire) şi (faza oprire) au
rolul de a asigura secvenţierea necesară; ele sunt introduse în BF atunci când se doreşte trecerea
la o anumită fază (după execuţia uneia din regulile R5, R6, R7 se va reveni la activarea regulii de
citire, R2). Regula R7 tratează valorile inutile în cazul problemei respective (pentru problema
considerată sunt utile doar valorile numerice sau simbolul EOF). Dacă regula R7 lipseşte şi în
fişier s-ar găsi o valoare nenumerică şi diferită de EOF, atunci nu se va mai asigura secvenţierea
pentru o nouă citire din fişier.

66
Exerciţiu
Experimentaţi funcţionarea programului de mai sus, creând în prealabil, cu ajutorul unui
editor de texte, fişierul intrare.dat. Verificaţi diferenţa între un program cu şi fără regula R7.

În CLIPS mai există câteva funcţii destinate lucrului cu fişiere. Pentru scrierea într-un
fişier, permiţând formatarea datelor de ieşire, există funcţia (similară lui “printf” din limbajul C):

(format <nume-logic> <şir-de-control> <<element-de-ieşire>>)

Argumentul nume-logic are aceeaşi semnificaţie ca în “printout”. Rezultatul pe care îl determină


funcţia “format” este un şir de caractere format din elementele-de-ieşire. Dacă dorim ca
elementele-de-ieşire să fie transmise numai pe terminal vom folosi ca nume-logic nil; în cazul în
care numele-logic folosit este t, atunci pe terminal rezultatul apare de două ori: o dată ca efect al
transmiterii pe terminal şi a doua oară ca rezultat al funcţiei “format”. Argumentul şir-de-control
trebuie să fie de tip şir de caractere şi va indica felul în care vor fi aşezate elementele-de-ieşire.
Drept element-de-ieşire poate fi orice expresie CLIPS care să determine ca rezultat un singur
câmp (în particular poate fi o constantă CLIPS). Argumentul şir-de-control constă dintr-un text şi
bistabile de control, totul inclus între ghilimele, pentru a forma un singur câmp de tip şir de
caractere. Textul va fi transmis nemodificat, iar bistabilele fixează felul în care vor fi aşezate
elementele-de-ieşire. În acest sens fiecare bistabil corespunde unui element-de-ieşire (primul
bistabil corespunde primului element-de-ieşire, al doilea bistabil se referă la al doilea element-
de-ieşire, ş.a.m.d.). Bistabilele vor fi precedate de semnul % şi vor avea forma generală:

%-M.Nx

cu următoarea semnificaţie. Semnul minus (-) este opţional: dacă este prezent alinierea
elementului-de-ieşire se face la stânga, iar în caz contrar alinierea se face la dreapta. În locul
literelor M şi N vor fi nişte numere, acestea având un caracter opţional. M specifică numărul de
poziţii alocate elementului-de-ieşire, în sensul că vor fi utilizate cel puţin M caractere (dacă
elementul-de-ieşire are mai mult de M caractere formatul se va expanda în mod corespunzător).
Dacă M începe cu cifra 0 atunci locurile libere din faţa elementului-de-ieşire (numai cele din
faţă, ceea ce presupune o aliniere la dreapta şi numai dacă elementul-de-ieşire este un număr)
sunt completate cu zerouri, iar în caz contrar se foloseşte spaţiul pentru locurile ce rămân libere
în faţă sau după elementul-de-ieşire. N specifică numărul de poziţii alocate părţii zecimale a
elementului-de-ieşire; dacă N nu apare, atunci în mod implicit, se folosesc pentru partea
zecimală a unui număr real un număr de 6 poziţii. Parametrul x trebuie să fie întotdeauna
prezent, putând fi una din valorile: d – elementul-de-ieşire trebuie să fie un număr, care va fi
transmis ca întreg lung (în acest caz N nu are nici o importanţă); f – elementul-de-ieşire va fi un
număr real, în virgulă mobilă; e - elementul-de-ieşire va fi un număr real, reprezentat ca putere a
lui 10, cu mantisă şi exponent; g – elementul-de-ieşire trebuie să fie un număr, care va fi
transmis în formatul cel mai general şi care este şi cel mai scurt; o – elementul-de-ieşire trebuie
să fie un număr, care va fi convertit în octal, fără semn (în acest caz N nu are nici o importanţă);
x – elementul-de-ieşire trebuie să fie un număr, care va fi convertit în hexazecimal, fără semn (în
acest caz N nu are nici o importanţă); s – elementul-de-ieşire va fi transmis ca şir de caractere;
dacă transmiterea se face într-un fişier atunci în acesta elementul-de-ieşire apare fără ghilimele,
în schimb rezultatul afişat pe terminal va fi între ghilimele (în acest caz N nu are nici o
importanţă); n – determină trecerea la o linie nouă.

Exerciţiu
Verificaţi funcţionarea comenzii “format” pe următoarele exemple:

(format t “Exemplu %n”)

67
(format nil “Exemplu %n”)
(format nil “Numarul este %10d” 1200)
(format nil “Numarul este %-10d” 1200)
(format nil “Numarul este %010d” 1200)
(format nil “%10.3f” 120.34)
(format nil “%10.3e” 120.34)
(format nil “%10.4e” 120.34)
(format nil “%10.6g %5o %3x” 120.34 90 255)
(format nil “%10s” abcdef)
(open w1.txt w1 “w”)
(format w1 “Ura! %10s %n %2s %10.3f %n” Traiasca A1 14.345)
(close)

Pentru citirea unei date, funcţia “read” prezentată anterior are dezavantajul că nu permite
decât preluarea unui singur câmp. Atunci când vrem să preluăm o linie întreagă (mai multe
valori), fie de la tastatură sau dintr-un fişier, vom folosi funcţia următoare:

(readline [<nume-logic>])

Această funcţie determină citirea unei întregi linii de intrare, conţinutul respectiv fiind preluat
sub forma unui singur câmp, de tip şir de caractere (adică inclus între ghilimele). Dacă
argumentul nume-logic este absent sau este t, citirea se face de la tastatură, iar dacă este prezent,
citirea se face din fişierul indicat prin numele-logic respectiv. La fel ca şi în cazul funcţiei
“read”, atunci când citirea se face dintr-un fişier şi s-a atins sfârşitul acestuia rezultatul returnat
este simbolul EOF. Modul de utilizare a acestei comenzi este ilustrat pe exemplul următor:

(defrule R8 “regula de citire a numelui si prenumelui”


=>
(printout t “Care este numele si prenumele ? ” crlf)
(assert (nume =(readline) ) ) )

Exerciţiu
Experimentaţi regula de mai sus, introducând de la tastatură mai multe câmpuri pe aceeaşi
linie. Observaţi care este deficienţa utilizării comenzii “readline”, deficienţă care va putea fi
eliminată conform celor prezentate în secţiunea următoare.

În ceea ce priveşte folosirea fişierelor pentru păstrarea programelor CLIPS, comenzile


“load” şi “save”, descrise anterior, sunt cele care permit încărcarea şi respectiv salvarea
definiţiilor de construcţii. Mai există două funcţii care permit încărcarea şi respectiv salvarea BF
într-un fişier. Astfel, pentru completarea BF există comanda:

(load-facts <nume-fişier>)

care determină încărcarea în BF a faptelor din fişierul specificat (argumentul nume-fişier poate fi
de tip şir de caractere sau simbol). Fişierul respectiv poate fi scris cu un editor de texte, dar
trebuie să conţină faptele organizate în formatul corespunzător. De exemplu, dacă în fişierul
f1.fap se găseşte conţinutul:

(temperatura punct A 152)


(presiunea punct A 7.3)
(curent-motor 6.7)

68
la comanda (load-facts f1.fap) cele trei fapte vor fi adăugate în BF.
Pentru salvarea faptelor existente la un moment dat în BF există comanda:

(save-facts <nume-fişier>)

Comanda de mai sus determină salvarea tuturor faptelor din BF în fişierul precizat prin
argument. Salvarea se face în formatul corespunzător faptelor în CLIPS, astfel că o folosire a
comenzii “save-facts” va permite o încărcare ulterioară a faptelor astfel salvate, prin folosirea lui
“load-facts”.

Exerciţiu
Scrieţi programul CLIPS care să salveze într-un fişier un anume grup de fapte din BF de la
momentul curent. În acest sens mai întâi se va prelua de la utilizator numele fişierului în care să
se facă salvarea şi primul câmp al faptelor care trebuie salvate (de exemplu, dacă utilizatorul
introduce de la tastatură: data, nume şi timp, atunci în fişierul indicat vor fi salvate toate faptele
din BF care încep cu unul din simbolurile data, nume, timp).

2. Funcţii CLIPS pentru şiruri de caractere şi pentru valori multicâmp

Există mai multe funcţii CLIPS ce permit prelucrarea şirurilor de caractere, simbolurilor şi
a valorilor cu mai multe câmpuri, în continuare fiind prezentate cele mai importante.

(str-assert <şir-de-caractere>)

Determină introducerea unui fapt în BF pe baza şirului de caractere primit ca argument.


Şirul de caractere trebuie să înceapă cu “(” şi să se termine cu “)”. Spaţiile din interiorul şirului
de caractere sunt delimitatorii care determină câmpurile din faptul ce se creează. Dacă dorim ca
un câmp al faptului ce se formează să fie de tip şir de caractere atunci trebuie să folosim înaintea
ghilimelelor caracterul de control \ (“back-slash”). Un exemplu de utilizare este următorul
(verificaţi experimental):

(str-assert “(marimi-masurate presiune temperatura \“volum-exterior\” )”)

Evident funcţia “str-assert” poate fi folosită nu numai la nivel superficial ci şi în reguli, în partea
de acţiune, când argumentul va putea fi obţinut şi prin folosirea unor variabile sau funcţii. Un
efect echivalent funcţiei “str-assert” îl are funcţia:

(assert-string <şir-de-caractere>)

O funcţie pentru prelucrarea valorilor de tip şir de caractere este:

(str-cat <<element>>)

Aceasta construieşte un singur şir de caractere din elementele care sunt parametri în
comanda respectivă; elementele furnizate ca parametri pot fi constante CLIPS, variabile simple
legate, sau funcţii ce determină ca rezultat un singur câmp. O funcţie similară există pentru
concatenarea constantelor de tip simbol:

(sym-cat <<element>>)

Rezultatul obţinut prin funcţia ”sym-cat” este un singur simbol, determinat din
concatenarea argumentelor funcţiei.

69
Exemplu
Se poate construi o nouă variantă a regulii de citire a numelui (R8), în care faţă de soluţia
dată anterior să se introducă în BF un fapt având un număr de câmpuri corespunzător numărului
de câmpuri introduse de la tastatură; aceasta se va face folosind funcţiile descrise anterior:

(defrule R9 “regula de citire a numelui si prenumelui in care acestea devin campuri


distincte”
=>
(printout t “Care este numele si prenumele ? ” crlf)
(str-assert (str-cat “(nume ” (readline) “)” ) ) )

Exerciţiu
Experimentaţi regula de mai sus, introducând de la tastatură mai multe câmpuri pe aceeaşi
linie. Observaţi forma faptului introdus în BF.

Există şi alte comenzi CLIPS dedicate constantelor de tip şir de caractere, permiţând:
extragerea unui subşir dintr-un şir de caractere (”sub-string”), aflarea poziţiei unui subşir într-un
şir de caractere (”str-index”), compararea a două constante de tip şir de caractere (”str-
compare”), determinarea lungimii unui şir de caractere (”str-length”); sintaxa şi modul de
utilizare pentru aceste comenzi se pot înţelege uşor din manualele limbajului CLIPS.

Există mai multe funcţii CLIPS pentru valori multicâmp, dintre care funcţia “mv-append”
a fost prezentată şi anterior; o reamintim aici:

(mv-append <<element>>)

Se creează o valoare multicâmp din elementele care sunt argumente. Un caz particular este acela
al apelului acestei funcţii cu zero argumente, caz în care se obţine valoarea multicâmp cu zero
câmpuri. Există o funcţie echivalentă pentru ”mv-append” şi anume:

(create$ <<element>>)

având aceeaşi funcţionare ca şi ”mv-append”. Alte funcţii pentru valori multicâmp sunt date în
continuare.

(mv-delete <indice-câmp> <valoare-multicâmp>)

Efectul este de ştergere (eliminare) a câmpului indicat prin indice-câmp din valoarea multicâmp
dată ca al doilea argument; acest ultim argument trebuie să fie o valoare multicâmp. De exemplu:
(mv-delete 1 (mv-append a b 1 2) ) va determina ca rezultat: (b 1 2); în acest
exemplu funcţia “mv-append” este folosită pentru a crea o valoare multicâmp; într-un program
CLIPS, valorile multicâmp vor putea fi obţinute şi din legări ale variabilelor multiple.

(delete$ <valoare-multicâmp> <indice-câmp1> <indice-câmp2> )

Efectul este de ştergere din valoarea multicâmp dată de primul argument a valorilor ale căror
poziţii în valoarea multicâmp sunt date de cele două argumente indici. De exemplu: (delete$
(create$ a b 1 2 3) 3 4) va determina ca rezultat: (a b 3). Dacă în comanda ”delete$”
argumentele indici sunt egale, atunci din valoarea multicâmp se şterge doar poziţia indicată prin
indicii daţi (se obţine un efect similar cu al comenzii ”mv-delete”).

70
(length$ <valoare-multicâmp>)

Determină ca rezultat numărul de câmpuri din valoarea multicâmp ce este dată ca argument.
Comanda poate fi dată şi sub forma (length <valoare-multicâmp>).

(nth$ <indice-câmp> <valoare-multicâmp)

Întoarce drept rezultat câmpul din cel de-al doilea argument ce are poziţia dată prin primul
argument. Dacă poziţia respectivă nu există rezultatul ce se obţine este nil. Comanda poate fi
dată şi sub forma (nth <indice-câmp> <valoare-multicâmp>).

(member$ <expresie> <valoare-multicâmp>)

Dacă rezultatul evaluării expresiei este o valoare cu un singur câmp şi acest câmp se regăseşte în
valoarea multicâmp ce este al doilea argument, atunci se obţine poziţia câmpului dat de primul
argument în valoarea multicâmp dată de cel de-al doilea argument. Dacă expresia se evaluează la
o valoare multicâmp şi această valoare multicâmp apare în valoarea multicâmp ce este al doilea
argument, atunci rezultatul este o valoare multicâmp cu două constante, reprezentând indicii de
start şi de sfârşit pentru includerea respectivă. Dacă nu se produce unul din cele două cazuri
menţionate, atunci rezultatul ce se obţine este FALSE. De exemplu: (member$ (create$ a b)
(create$ 1 2 a s a b 3) ) va produce rezultatul (5 6).

(subsetp <valoare-multicâmp1> <valoare-multicâmp2>)

Întoarce rezultatul TRUE dacă câmpurile care apar în primul argument se regăsesc toate printre
câmpurile din cel de-al doilea argument şi FALSE în caz contrar (ambele argumente trebuie să
fie valori multicâmp). Este de observat că valoarea multicâmp cu zero câmpuri se regăseşte în
orice altă valoare multicâmp. De exemplu: (subsetp (mv-append) (mv-append 1 2 3)
) va determina rezultatul TRUE.

(subseq$ <valoare-multicâmp> <indice1> <indice2>)

Determină extragerea din valoarea multicâmp ce este primul argument a poziţiilor delimitate de
indicii specificaţi, adică se obţine o parte din valoarea multicâmp ce este primul argument. De
exemplu: (subseq$ (create$ 1 2 a s a b 3) 3 6) determina ca rezultat: (a s a b).

(replace$ <valoare-multicâmp> <indice1> <indice2> <<expresie>>)

Determină înlocuirea în valoarea multicâmp reprezentând primul argument a valorilor dintre


poziţiile specificate cu rezultatul evaluării expresiei sau expresiilor ce apar după argumentele
indici. De exemplu: (replace$ (create$ 1 2 a s a b 3) 3 6 x x (create$ 12 13))
determină ca rezultat: (1 2 x x 12 13 3).

(insert$ <valoare-multicâmp> <indice> <<expresie>>)

Determină inserarea valorii sau valorilor obţinute din evaluarea ultimelor argumente în valoarea
multicâmp ce este primul argument de la poziţia indicată prin argumentul indice. Acest argument
trebuie să fie un număr natural, mai mare sau egal cu 1; dacă indicele este 1, atunci inserarea se
face de la începutul valorii multicâmp; dacă indicele are o valoare mai mare decât lungimea
valorii multicâmp atunci inserarea se face la sfârşit. De exemplu: (insert$ (create$ 1 2 a s a b 3)
3 asa (create$ 12 13) ) determină ca rezultat: (1 2 asa 12 13 a s a b 3).

71
(first$ <valoare-multicâmp>)

Determină primul câmp al valorii multicâmp ce este argument.

(rest$ <valoare-multicâmp>)

Determină ceea ce rămâne din valoarea multicâmp dacă se elimină primul câmp. De exemplu:
(rest$ (create$ a s 1 3)) determină rezultatul: (s 1 3).

(str-explode <şir-de-caractere>)

Converteşte un şir de caractere într-o valoare multicâmp (delimitatorii sunt spaţiile din interiorul
şirului de caractere). Funcţia inversă este:

(str-implode <valoare-multicâmp>)

care converteşte o valoare multicâmp într-un singur câmp, de tip şir de caractere. Comenzi
echivalente pentru ”str-explode” şi ”str-implode” sunt: (explode$ <şir-de-caractere>),
respectiv (implode$ <valoare-multicâmp>).

3. Elemente procedurale de control al execuţiei programului

CLIPS posedă câteva funcţii de tipul celor care în limbajele clasice permit controlul
execuţiei programelor. Deşi acestea pot fi folosite şi la nivel superficial, ele îşi găsesc utilitatea
în scrierea unor reguli care pot deveni mai eficiente prin folosirea în partea de acţiune a funcţiilor
procedurale de control al execuţiei; de altfel, funcţiile care sunt prezentate în continuare pot fi
folosite în reguli numai în partea de acţiune (nu şi în partea de condiţie). Există în CLIPS trei
asemenea funcţii, descrise în cele ce urmează.

(if <predicat> then <<<acţiune>>> [else <<<acţiune>>>])

Când se întâlneşte această funcţie (în partea dreaptă a unei reguli sau la nivel superficial),
mai întâi se evaluează predicatul. Dacă rezultatul evaluării este TRUE se execută
acţiunea/acţiunile de după “then”, iar dacă rezultatul evaluării predicatului este FALSE şi dacă
există clauza “else”, atunci se execută acţiunea/acţiunile de după “else”. Dacă predicatul
determină rezultatul FALSE şi nu există clauza “else” atunci nu se execută nici o acţiune şi se
trece la următoarea comandă.
Funcţia “if” poate fi utilă pentru efectuarea unor teste în partea de acţiune a unei reguli,
evitând în acest fel scrierea unor reguli suplimentare. Ca exemplu, regula de mai jos are rolul de
a ghida continuarea unui program, în funcţie de conţinutul unui fapt din BF.

(defrule R10
?a <- (continuare ?x)
=>
(retract ?a)
(if (or (eq ?x da) (eq ?x Da) (eq ?x DA) )
then (assert (faza lucru) )
else (assert (faza stop) ) ) )

Exerciţiu
Înlocuiţi regula de mai sus cu câteva reguli care să determine aceleaşi consecinţe şi care să
nu mai folosească funcţia “if”.

72
(while <predicat> [do] <<<acţiune>>>)

Cuvântul do este opţional, iar acţiunea/acţiunile care urmează constituie corpul ciclului.
Mai întâi se evaluează predicatul; dacă rezultatul determinat este TRUE, atunci se execută
corpul, iar dacă predicatul se evaluează la FALSE, atunci se trece mai departe, la următoarea
comandă CLIPS. După executarea corpului ciclului se testează din nou predicatul, ş.a.m.d.

Exemplu
Regula următoare preia de la operator informaţia cu privire la continuarea unui program,
introducând în BF un fapt corespunzător. În plus, dacă utilizatorul nu răspunde cu unul din
cuvintele DA sau NU, programul intră într-o buclă, creată prin folosirea funcţiei “while”.

(defrule R11
?a <- (faza verificare-continuare)
=>
(retract ?a)
(printout t “Continuati ? ” crlf)
(bind ?x (read))
(while (and (neq ?x DA)(neq ?x NU) )
do (printout t “Continuati ? (raspundeti cu DA sau NU) ”
crlf)
(bind ?x (read) ) )
(if (eq ?x DA)
then (assert (faza lucru) )
else (assert (faza stop) ) ) )

Observaţie
Funcţiile “if” şi “while” nu sunt obişnuite într-un program bazat pe reguli. Scrierea unui
întreg program, într-o variantă procedurală, în partea de acţiune a unei reguli, prin folosirea lui
“if” şi “while” determină pierderea funcţionalităţii specifice pentru un program bazat pe reguli,
respectiv a unui SE. Într-un SE funcţiile “if” şi “while” vor fi folosite numai pentru efectuarea
unor teste şi bucle simple.

O altă funcţie de control al execuţiei programului este:

(halt)

Aceasta determină oprirea execuţiei unui program CLIPS (se opreşte execuţia regulilor) şi se
revine la nivel superficial. Agenda va continua să păstreze regulile ce eventual existau în ea,
astfel că o comandă “run” care va urma le va putea pune în execuţie. În acest fel, comanda “halt”
poate fi utilă în depanare; după o oprire datorată acestei comenzi utilizatorul poate urmări starea
agendei, a BF, pentru ca apoi să reia rularea programului (în acelaşi sens pot fi ataşate şi punctele
de oprire ”break-point”, după cum s-a arătat anterior). Ca exemplu, s-a rescris regula anterioară
în aşa fel încât să determine oprirea programului dacă utilizatorul introduce un câmp diferit de
simbolul DA şi diferit de simbolul NU.

(defrule R12
?a <- (faza verificare-continuare)
=>
(retract ?a)
(printout t “Continuati ? ” crlf)

73
(bind ?x (read) )
(if (and (neq ?x DA) (neq ?x NU) )
then (halt) )
(if (eq ?x DA)
then (assert (faza lucru) )
else (assert (faza stop) ) ) )

4. Comenzi utilitare în CLIPS

Există o serie de comenzi care pot ajuta utilizatorul în interfaţarea cu programele CLIPS.
Astfel se poate cere executarea unei comenzi a sistemului de operare prin funcţia:

(system <<<valoare>>>)

Argumentele trebuie să fie constante CLIPS sau expresii care să furnizeze ca rezultat un singur
câmp. Efectul este acela de a trece controlul sistemului de operare şi de a executa comanda care
se obţine prin concatenarea argumentelor care apar în comanda “system”; în acest sens, pentru a
crea comanda de executat de către sistemul de operare se concatenează într-un singur şir de
caractere toate valorile date prin argumente. Această comandă este utilă, de exemplu, atunci când
se lucrează cu versiunea mediului CLIPS destinată sistemului de operare DOS. De asemenea,
folosind comanda ”system” pot fi lansate din CLIPS aplicaţii realizate în alte limbaje de
programare.

Se poate cere execuţia unui fişier de tip .bat prin comanda:

(batch <nume-fişier>)

în care argumentul nume-fişier trebuie să fie de tip simbol sau şir de caractere. Efectul va fi acela
de execuţie a comenzilor din fişierul indicat prin argument, fişier care trebuie să fie de tip .bat.
Această comandă va fi folosită de obicei pentru a înlocui o secvenţă de comenzi care se execută
în mod repetat, de exemplu la începutul lucrului în CLIPS, cu o singură comandă “batch”, care
va substitui întreaga secvenţă respectivă. Într-un fişier de tip .bat poate fi înscrisă orice comandă,
funcţie, definiţie de construcţie, din CLIPS, ca şi răspunsurile la comenzile “read” sau
“readline”. Totuşi este bine ca definiţiile de construcţii să nu fie plasate în fişiere de tip .bat ci în
fişiere de tip .clp, pe care să le încărcăm cu comanda “load”, pentru că o greşeală făcută într-un
fişier .bat este mai greu de depistat şi depanat, decât atunci când lucrăm cu fişiere clp. Comanda
“batch” returnează valoarea TRUE dacă fişierul .bat respectiv a putut fi executat cu succes şi
FALSE în caz contrar.
Pentru exemplificare, presupunem următorul dialog efectuat de sub interpretorul CLIPS:

(load reguli1.clp) ¶
(load reguli2.clp) ¶
(reset) ¶
(run) ¶
Cate iteraţii ? 10 ¶
Valoarea de start ? 2¶

în care 10 şi 2 sunt valori introduse de la tastatură, ele fiind cerute de comenzi “read” executate
din regulile rulate. Comenzile de mai sus, ca şi informaţiile furnizate, pot fi plasate într-un fişier
de tip .bat; fie acesta fişierul f1.bat, care va avea următorul conţinut:

74
(load reguli1.clp)
(load reguli2.clp)
(reset)
(run)
10
2

În acest caz toată conversaţia anterioară este înlocuită de comanda (batch f1.bat).
O facilitate suplimentară privind fişierele de tip .bat este aceea că interpretorul CLIPS
poate executa în mod automat un fişier .bat la lansare. O asemenea facilitate poate fi utilă, de
exemplu, pentru versiunea mediului CLIPS destinată sistemului de operare DOS. Pentru aceasta
linia de comandă de chemare a interpretorului CLIPS va fi:

clips -f <nume-fişier>

unde există un spaţiu între simbolul clips şi –f şi un spaţiu între –f şi nume-fişier, iar nume-fişier
poate fi de tip simbol sau şir de caractere, reprezentând numele unui fişier de tip .bat. Efectul va
fi acela că se va lansa interpretorul CLIPS şi se vor executa comenzile din fişierul precizat, cu
restricţia că în acest caz fişierul de tip .bat poate conţine numai comenzi, nu şi informaţii cerute
de execuţia funcţiilor “read” sau “readline”.

Înregistrarea într-un fişier a unei sesiuni de lucru cu interpretorul CLIPS se poate obţine
prin comanda:

(dribble-on <nume-fişier>)

După execuţia acestei comenzi, în timp ce suntem sub interpretorul CLIPS, tot ceea ce se
introduce de la tastatură şi tot ceea ce se transmite pe monitor va fi înregistrat şi în fişierul
specificat în comanda “dribble-on”. Pentru a anula acest efect vom folosi comanda:

(dribble-off)

Exerciţii

1. Răspundeţi la următoarele întrebări:


• Care e deosebirea între argumentele nume-fişier şi nume-logic într-o comandă “open” ?
• Care este deosebirea între un fişier deschis printr-o comandă “open” cu argumentul
acces “w” şi un fişier deschis cu argumentul acces “a” ?
• Care este deosebirea între comenzile (read) şi (read t) ? Dar între (format t “%f ”
12) şi (format nil “%f” 12) ?
• Care este deosebirea între comenzile “load” şi “load-facts” şi respectiv între “save” şi
“save-facts” ?
• Care este rezultatul următoarei comenzi:
(member$ (length$ (mv-append 1 2 3) ) (mv-append a 1 c 3 d) ); dar al
comenzii:
(nth$ 5 (explode$ “a b c d e f 6”)).

2. Înlocuiţi regulile R11 şi R12 din secţiunea 3 cu variante de programe CLIPS care să nu
folosească funcţiile “if”, “while”.

Probleme recapitulative
1. Rescrieţi programul CLIPS care să materializeze următoarea funcţie binară:

75
F ( x , y ,z )= x⋅ y⋅ zUx⋅ y⋅ z

2. Cu referire la un exerciţiu anterior, în care apărea următoarea regulă:

(defrule R
(tatal-lui ?copil este ?tata)
(tatal-lui ?tata este ?bunic)
=>
(printout t “Bunicul lui ” ?copil “este” ?bunic crlf) )

rescrieţi regula de mai sus în aşa fel încât să nu poată fi satisfăcute ambele tipare de un singur
fapt din BF; de exemplu, în forma de mai sus, faptul (tatal-lui Ion este Ion) ar activa
regula, potrivindu-se pe ambele tipare, ceea ce nu trebuie să se întâmple.

3. Fie una din regulile folosite anterior, reluată mai jos:

(defrule Rr
(student ?nume1 AC ? ?gr&1501|1502 ?dom&~Iasi)
(student ?nume2&~?nume1 AC ? ?gr ?dom | Bucuresti)
=>
(printout t “Studentii ” ?nume1 “ si ” ?nume2 “ din grupa ” ?gr
“ indeplinesc conditiile cerute” crlf) )

Aceasta determină o pereche de fapte din BF ce satisfac anumite condiţii; deficienţa care apare
este că o pereche de fapte poate activa regula de două ori. De exemplu, faptele: (student Ion
AC 5 1501 Bucuresti) şi (student Gelu AC 5 1501 Bucuresti) activează regula
Rr de două ori şi la rulare determină afişarea mesajului de două ori. Eliminaţi această deficienţă.

4. Scrieţi un program CLIPS care să determine valoarea unei proprietăţi specificate pentru
un nod dat al unei reţele semantice (în cazul în care pentru nodul respectiv nu se poate determina
proprietatea precizată se va transmite un mesaj adecvat). În prealabil se va stabili felul în care se
memorează în CLIPS o reţea semantică; programul trebuie să fie conceput pentru a rezolva cazul
general, iar pentru verificarea funcţionării se va alege un caz particular, de exemplu pentru un
univers de discurs format din corpuri cu forme geometrice simple.

76
Lucrarea nr. 7

Fapte CLIPS de tip structurat


1. O clasificare a faptelor în CLIPS

În CLIPS există două tipuri de fapte: nestructurate (toate cele folosite până acum) şi
structurate. Într-un fapt nestructurat ordinea câmpurilor poate fi oarecare, aceasta fiind aleasă de
programator, dar odată ordinea fixată, ea trebuie respectată în întreg programul CLIPS. Altfel
spus, într-un fapt nestructurat contează atât conţinutul fiecărui câmp, cât şi ordinea câmpurilor.
De exemplu, în urma comenzii (assert (a b c) (a c b)) în BF vor fi introduse două fapte
distincte. În acest sens, faptele nestructurate se numesc şi fapte de tip ordonat, în ideea că au
câmpurile aşezate într-o ordine bine stabilită şi, în principiu, pentru a avea acces la o anumită
informaţie programatorul trebuie să ştie al câtelea câmp din fapt conţine informaţia respectivă
(atunci când ordinea câmpurilor nu este cunoscută a priori folosirea variabilelor multiple poate
rezolva găsirea unui anumit câmp). Spre deosebire de acest caz, în faptele structurate ordinea în
care sunt plasate informaţiile nu mai are importanţă; de aceea, acestea se numesc şi fapte de tip
neordonat.
Faptele structurate din CLIPS sunt asemănătoare cu tipurile structurate din programarea
convenţională (de exemplu, tipul articol din Pascal sau C), urmărind şi caracteristicile modelului
de cadru din inteligenţa artificială.

2. Definirea faptelor structurate

Pentru a putea fi folosit un fapt de tip structurat trebuie mai întâi definit, prin comanda:

(deftemplate <nume-deftemplate> [<comentariu>]


<<faţetă-cu-un-câmp>> (1)
<<faţetă-multicâmp>> )

Observăm respectarea tipicului pentru definirile de construcţii din CLIPS şi asemănarea


cu definirea faptelor nestructurate prin “deffacts”.
Faptul structurat care se defineşte va putea fi identificat prin argumentul nume-
deftemplate; acesta trebuie să fie un simbol. Comentariu are aceeaşi formă ca şi în toate celelalte
definiţii de construcţii din CLIPS, adică este un şir de caractere.
Expresia (1) trebuie înţeleasă în sensul că un fapt structurat poate fi definit cu oricâte
faţete cu un singur câmp şi oricâte faţete multicâmp, aşezate în orice ordine. În definiţia de mai
sus faţeta cu un câmp trebuie să respecte următoarea sintaxă:

(field <nume-faţetă> <<specificare-faţetă>>)

sau

(slot <nume-faţetă> <<specificare-faţetă>>)

iar faţeta multicâmp:

(multifield <nume-faţetă> <<specificare-faţetă>>)

sau

(multislot <nume-faţetă> <<specificare-faţetă>>)

77
Argumentul nume-faţetă trebuie să fie un simbol. Prin argumentele specificare-faţetă se
vor preciza caracteristicile informaţiei care poate fi păstrată într-o faţetă a unui fapt structurat.
Există mai multe tipuri de specificări de faţetă, ele putând fi împărţite în două categorii:
specificări pentru valoarea cea mai aşteptată şi specificări de restricţionare.

A. Specificarea de valoare cea mai aşteptată este cea care permite în CLIPS
efectuarea raţionamentului implicit. Astfel, dacă se ştie valoarea cea mai plauzibilă pentru o
faţetă, această valoare va fi dată într-o specificare “default”. Totodată, specificarea “default” are
rolul de a furniza valoarea unei faţete atunci când utilizatorul nu dă o valoare explicită pentru
faţeta respectivă. Sintaxa acestei specificări este:

(default <valoare-aşteptată>)

cu observaţia că argumentul valoare-aşteptată va avea un singur câmp, sau 0 ÷ n câmpuri, după


cum specificarea respectivă face parte dintr-o faţetă cu un câmp, respectiv una multicâmp.
Drept valoare aşteptată se poate folosi şi ?NONE. Când o faţetă are specificarea (default
?NONE), utilizatorul este obligat ca de fiecare dată când introduce un fapt structurat în BF să
precizeze valoarea faţetei respective.

Exemplu:

(deftemplate microprocesor “acesta este un fapt structurat”


(field nume)
(field magistrala (default 8)) )

(assert (microprocesor (nume 8080) ) (microprocesor (nume 80286) (magistrala 16) ) )

În acest exemplu s-a definit faptul structurat cu numele microprocesor, având două faţete:
nume şi magistrala. Pentru faţeta magistrala s-a introdus şi o specificare default. Aceasta
determină ca, atunci când utilizatorul nu precizează valoarea explicită a acestei faţete, sistemul să
introducă de la sine valoarea dată în “default”, adică valoarea 8 în cazul nostru. Dacă pentru
faţeta magistrala este dată o valoare explicită, atunci valoarea din default este ignorată. Toate
acestea se pot verifica prin comanda “assert” propusă în exemplul de mai sus. Este de observat
că faptele structurate se introduc în BF tot prin comanda “assert”; într-o asemenea comandă
trebuie manevrate cu grijă parantezele: fiecare fapt structurat trebuie inclus între paranteze şi
fiecare faţetă dintr-un fapt structurat (precizată prin numele ei) trebuie şi ea inclusă într-o
pereche de paranteze.

B. Specificările de restricţionare sunt de patru feluri: de tip, de valori permise, de


domeniu şi de cardinalitate.
Specificarea de restricţionare de tip are sintaxa:

(type <<<tip>>> )

unde argumentul tip poate fi unul din următoarele: INTEGER, FLOAT, NUMBER, SYMBOL,
STRING, LEXEME (tipurile trebuie scrise în mod obligatoriu cu litere mari) sau poate fi
?VARIABLE. Atunci când într-o faţetă apare o asemenea specificare, informaţia care va putea fi
păstrată în faţetă este restricţionată la tipul indicat. Semnificaţiile sunt: INTEGER înseamnă
restricţionare la numere întregi, FLOAT la numere reale, NUMBER la orice fel de numere,
SYMBOL restricţionare la simboluri CLIPS, STRING la şiruri de caractere, LEXEME la
simboluri sau şiruri de caractere, iar dacă se foloseşte ?VARIABLE atunci nu se produce nicio
restricţionare de tip (orice tip este admis, situaţia fiind aceeaşi cu aceea în care specificarea de tip

78
nu este folosită). Într-o specificare de tip pot fi prevăzute şi mai multe argumente tip. De
exemplu, dacă în faptul structurat microprocesor vrem ca numele să poată fi numai de tip simbol
sau şir de caractere, atunci putem reface definiţia anterioară:

(deftemplate microprocesor “acesta este un fapt structurat”


(field nume (type STRING SYMBOL) )
(field magistrala (default 8)) )

Această definiţie este echivalentă cu următoarea:

(deftemplate microprocesor “acesta este un fapt structurat”


(field nume (type LEXEME) )
(field magistrala (type ?VARIABLE) (default 8)) )

Atunci când valoarea memorată într-o faţetă vrem să fie sau un întreg sau un real vom
putea folosi (type NUMBER); dacă folosim (type FLOAT) atunci o valoare întreagă va
însemna eroare, ca în exemplul următor:

(deftemplate oscilator
(field frecventa (type FLOAT) ) )

La comanda (assert (oscilator (frecventa 10))) se va semnala eroare de tip, pentru că


valoarea folosită pentru faţeta frecventa este un întreg şi nu un număr real.

Specificarea de restricţionare de valori permise are sintaxa de una din următoarele forme:

(allowed-symbols {<înşiruire-de-simboluri>, ?VARIABLE})


(allowed-strings {<înşiruire-de-şiruri-de caractere>, ?VARIABLE})
(allowed-lexemes {<înşiruire-de-simboluri-sau-şiruri-de-caractere>, ?VARIABLE})
(allowed-integers {<înşiruire-de-numere-întregi>, ?VARIABLE})
(allowed-floats {<înşiruire-de-numere-reale>, ?VARIABLE})
(allowed-numbers {<înşiruire-de-numere-întregi-sau-reale>, ?VARIABLE})
(allowed-values {< înşiruire-de-valori>, ?VARIABLE})

Restricţionarea care se face în acest caz este la valorile din înşiruirea ce este argument,
trebuind evident să existe o concordanţă între elementele din înşiruire şi varianta “allowed” care
se foloseşte; de exemplu, se poate utiliza (allowed-symbols microprocesor microcontroler),
dar nu se poate folosi (allowed-symbols 12 -34). Specificarea “allowed-values” face o
restricţionare strictă, la valorile din înşiruirea care urmează; în celelalte cazuri restricţia apare
numai pentru tipul specificat, aşa cum se arată în continuare.

Exemplu:

(deftemplate microprocesor1
(field nume (allowed-symbols I8086 I80286) ) )

(deftemplate microprocesor2
(field nume (allowed-values I8086 I80286) ) )

Pe baza acestor două definiţii putem da comanda:

(assert (microprocesor1 (nume I80286)) (microprocesor1 (nume 80286)) )

79
Vom constata că ambele fapte sunt introduse în BF. Explicaţia este aceea că primul fapt
satisface restricţia de simboluri impusă, iar al doilea are valoarea faţetei nume un număr, iar
restricţia specificată se referă numai la simboluri. În acest sens, dacă se dă comanda:

(assert (microprocesor1 (nume I86)) )

se va semnala eroare, deoarece valoarea faţetei nume este un simbol şi acesta nu este printre cele
din înşiruirea “allowed-symbols”. Tot eroare se obţine şi în cazul comenzii:

(assert (microprocesor2 (nume 8086)) )

pentru că la faptul structurat “microprocesor2” s-a făcut o restricţionare strictă, cu specificarea


“allowed-values”, ceea ce înseamnă că numai cele două valori care apar în înşiruirea respectivă
pot fi folosite ca valori ale faţetei nume.
Dacă într-o specificare “allowed” în locul unei înşiruiri apare ?VARIABLE, atunci nu
se face nicio restricţionare asupra tipului respectiv. De exemplu, o definiţie de forma:

(deftemplate identificator (field nume (allowed-values ?VARIABLE) ) )

nu introduce nicio restricţie asupra valorii care va fi introdusă în faţeta nume. Conform cu
sintaxa pentru faţete, specificările de restricţionare de tip şi de valori permise pot fi şi combinate
ca în exemplul următor:

(deftemplate identificator
(field nume (type SYMBOL) (allowed-symbols I1 I2 I3) )
(field val1 (type NUMBER SYMBOL) (allowed-symbols a1 a2) )
(field val2 (type LEXEME) )
(multifield val3 (type STRING) (allowed-values “1” “2”) ) )

Exerciţiu Comentaţi restricţiile care apar în definiţia de mai sus. Introduceţi o definiţie
logic echivalentă cu aceea a faptului identificator şi care să aibă mai puţine specificări de
restricţionare. Introduceţi în BF fapte structurate de tip identificator.

Specificarea de restricţionare de domeniu are sintaxa:

(range <minim> <maxim>)

Aceasta determină intervalul în care trebuie să se încadreze valorile faţetei respective,


atunci când tipul acesteia este INTEGER, FLOAT sau NUMBER. Dacă tipul nu este unul din
acestea trei şi se foloseşte specificarea “range”, atunci aceasta nu determină nicio restricţie.
Argumentele minim şi maxim pot fi numere întregi sau reale, sau pot fi ?VARIABLE. Dacă se
foloseşte ?VARIABLE drept argument minim atunci limita inferioară a domeniului astfel stabilit
este -∞, iar dacă ?VARIABLE se foloseşte drept argument maxim, atunci limita superioară a
domeniului de restricţionare este +∞.

Exemplu:

(deftemplate numar (field valoare (range ?VARIABLE 50.5) ) )


(deftemplate parametru (field valoare (type INTEGER) (range 0 1000) ) )

Se poate observa că am folosit acelaşi nume de faţetă - valoare - în două fapte structurate,
ceea ce nu determină nicio legătură între acestea. În faptul numar, faţeta valoare poate fi orice

80
număr în intervalul (-∞, 50,5] (aici neimpunându-se o restricţie de tip, se poate şi ca faţeta
valoare să nu fie număr, situaţie în care restricţia de domeniu este inoperantă). În faptul
parametru faţeta valoare poate fi orice număr întreg din intervalul [0, 1000]. De remarcat că
domeniile se stabilesc ca intervale închise.

Exerciţiu Experimentaţi comportarea celor două definiţii de fapte structurate anterioare,


introducând, prin comenzi “assert” sau “deffacts”, câteva fapte adecvate în BF.
Este de notat că specificarea range nu poate fi utilizată în acelaşi timp cu una din
specificările: allowed-values, allowed-numbers, allowed-integers, sau allowed-floats.

Specificarea de restricţionare de cardinalitate restricţionează numărul de câmpuri care pot fi


introduse într-o faţetă de tip multicâmp. Este evident că o asemenea specificare va putea fi
folosită numai pentru o faţetă de tip multicâmp. Sintaxa este:

(cardinality <min> <max>)

Parametrii min şi max pot fi numere întregi sau pot fi ?VARIABLE. Ei precizează numărul
minim, respectiv numărul maxim de câmpuri care pot fi păstrate în faţeta multicâmp respectivă.
Dacă pentru min în locul unui întreg se foloseşte ?VARIABLE, atunci înseamnă că numărul
minim posibil de câmpuri este fixat la zero. Dacă ?VARIABLE se foloseşte pentru max, atunci
numărul maxim de câmpuri din faţetă este +∞. Dacă specificarea de cardinalitate nu este
prezentă între specificările unei faţete de tip multicâmp, atunci, în mod implicit, se stabileşte că
numărul de câmpuri poate fi între 0 şi +∞.

3. Calculul valorii celei mai aşteptate a unui fapt structurat

O problemă care poate apare este aceea a determinării pentru o faţetă a valorii celei mai
aşteptate (default), atunci când specificarea “default” nu există pentru acea faţetă. Într-un
asemenea caz, valoarea cea mai aşteptată se va deduce de către interpretorul CLIPS după
următoarele reguli, care se aplică în ordinea în care sunt enumerate:
1. Tipul pentru valoarea cea mai aşteptată este ales din lista de tipuri permise pentru faţetă,
în următoarea ordine de preferinţe: SYMBOL, STRING, INTEGER, FLOAT.
2. Dacă tipul rezultat după regula 1 are o specificare “allowed” (de exemplu, faţeta are
specificările (type INTEGER) şi (allowed-integers -4 0 34) ) atunci prima valoare din lista
“allowed” este aleasă ca valoare default (în cazul considerat, valoarea default ar fi –4).
3. Dacă tipul fixat pentru faţetă este INTEGER, FLOAT sau NUMBER şi există specificarea
“range” atunci valoarea minimă a domeniului devine valoare default, dacă nu este -∞; dacă
valoarea minimă a domeniului este -∞ şi valoarea maximă a domeniului nu este +∞, atunci
valoarea maximă a domeniului se consideră ca valoare default.
4. Dacă faţeta nu are tipul precizat, dar are o specificare “allowed-symbols”, atunci prima
valoare din lista respectivă se ia ca valoare default.
5. Dacă valoarea default nu este determinată prin una din regulile de mai sus, atunci ea se
fixează în funcţie de tipul faţetei, după cum urmează: pentru tipul STRING este “” (şirul de
caractere vid), pentru INTEGER este 0, pentru FLOAT este 0.0, iar pentru tipul SYMBOL sau
atunci când tipul nu este precizat este simbolul nil.
6. Dacă faţeta este de tip multicâmp atunci valoarea multicâmp cu 0 câmpuri este folosită,
dacă nu suntem în cazul în care pentru faţeta respectivă s-a fixat printr-o specificare de
cardinalitate un număr minim de câmpuri mai mare ca 0. Într-un asemenea caz valoarea default
va avea un număr de câmpuri egal cu minimum permis prin specificarea “cardinality” şi valoarea
din fiecare câmp va fi aceea calculată după regulile 1 ÷ 5, ca şi în cazul în care faţeta respectivă
ar fi de tipul cu un câmp.

81
Exerciţiu Fie următoarea definiţie de fapt structurat:

(deftemplate Microprocesor
(multifield nume (type LEXEME) )
(field bus (allowed-values 1 4 8 16 32) )
(field viteza (type STRING NUMBER) )
(field tehnologie (allowed-symbols CMOS NMOS PMOS) )
(field putere (type NUMBER) (range ?VARIABLE 10) )
(field stare (allowed-values actual depasit) (default actual) ) )

Explicaţi conţinutul faptului care se introduce în BF la comanda: (assert (Microprocesor) ).

4. Introducerea faptelor structurate în baza de fapte

Introducerea faptelor structurate în BF se face prin aceleaşi procedee ca şi pentru faptele


nestructurate: prin comenzi “assert” (date la nivel superficial sau din partea de acţiune a
regulilor), respectiv prin comenzi “deffacts” (date la nivel superficial sau încărcate din fişiere).
Deosebirea faţă de faptele nestructurate este accea că pentru a putea folosi un fapt structurat într-
o operaţie de introducere în BF este necesar ca în prealabil acesta să fi fost definit printr-o
definiţie “deftemplate”. Aceasta, fiind definiţie de construcţie, poate fi dată la nivel superficial
sau înscrisă în fişiere. Aşa cum se vede şi din exemplele anterioare, într-o comandă “assert” (şi la
fel stau lucrurile şi pentru comanda “deffacts”) se va prevedea, într-o pereche de paranteze,
numele faptului structurat şi 0 ÷ n din faţetele acestuia, unde n este numărul de faţete care apare
la definiţia faptului structurat respectiv. Faţetele care sunt prevăzute sunt acelea pentru care vrem
să fixăm o valoare diferită de valoarea “default”, în timp ce faţetele care nu sunt prezente vor lua
în mod implicit valoarea “default”. Fiecare faţetă trebuie inclusă într-o pereche de paranteze.

Exemplu:

Având definiţia faptului structurat din exerciţiul anterior, exemple de introduceri în BF ale
unor fapte structurate cu numele Microprocesor sunt:

(assert (Microprocesor (putere 5) (nume Intel I80286) (bus 16) ) )


(deffacts ini (Microprocesor (nume I8080) (stare depasit) (bus 8) ) )

Experimentaţi aceste comenzi.

Observaţii Conform celor ştiute pentru comanda “deffacts”, aceasta determină introducerea
faptelor din interiorul ei în BF numai după un “reset”. Este de notat că în cazul faptelor
structurate ordinea faţetelor nu are nicio importanţă, aşa cum se vede şi din cele două situaţii de
mai sus.
Pentru faptele structurate există câteva noi posibilităţi de introducere în BF. Astfel, atunci
când în BF există deja un fapt structurat şi vrem să introducem unul asemănător, putem folosi
comanda:

(duplicate <index-fapt-structurat> <<faţetă-de-modificat>>)

Efectul este acela de introducere a unui nou fapt structurat în BF, cu acelaşi conţinut ca şi
cel al faptului deja existent şi care are indexul precizat prin primul argument, mai puţin faţetele
care apar explicit în comanda respectivă şi pentru care se folosesc valorile date în comandă.

82
Exemplu :
Având în BF faptul structurat “Microprocesor” cu faţeta nume Intel I80286 (acesta fiind
plasat la identificatorul f-1), dacă ne interesează să introducem în BF un nou fapt structurat, cu
acelaşi conţinut, mai puţin faţetele nume şi putere, care trebuie să devină I80386, respectiv 3,
vom putea folosi comanda:
(duplicate 1 (nume I80386) (putere 3) )
Observaţii Dacă se foloseşte o comandă “duplicate” în care nu apare nicio faţetă de
modificat, atunci rezultatul depinde de starea bistabilului privind faptele duplicat. Nefiind
definiţii de construcţii, comenzile “duplicate” pot fi date la nivel superficial, sau în partea de
acţiune a regulilor.

O altă posibilitate de introducere în BF a faptelor structurate este cea dată de comanda:


(modify <index-fapt-structurat> <<faţetă-de-modificat>>)
Efectul este acela de ştergere din BF a faptului structurat având indexul precizat prin primul
argument al comenzii şi de introducere a unui nou fapt, cu un conţinut identic cu cel al faptului
şters, mai puţin faţetele de modificat, care iau valorile indicate în comandă. Astfel, dacă în BF, la
identificatorul f-2, se găseşte faptul “Microprocesor”, cu faţeta nume I8080 şi faţeta stare
depasit, comanda: (modify 2 (stare actual) ) va determina înlocuirea în BF a faptului
respectiv cu un altul, în care se produce schimbarea valorii faţetei stare. Folosirea comenzii
“modify” fără nicio faţetă de modificat este posibilă şi va determina ştergerea faptului identificat
prin argumentul “index-fapt-structurat” şi reintroducerea aceluiaşi fapt în BF, cu acelaşi conţinut,
dar într-o nouă poziţie (la următorul index liber). Mai menţionăm că, în conformitate cu sintaxa
dată, în comenzile “modify” sau “duplicate” poate fi implicat un singur fapt structurat.

5. Corelarea faptelor structurate cu regulile

Faptele structurate se corelează cu regulile la fel ca faptele nestructurate: prezenţa lor în BF


determină activarea regulilor, prin procesul de potrivire. Deosebirea care apare este dată de aceea
că ordinea faţetelor nu are importanţă (în contrast cu câmpurile din faptele nestructurate, a căror
ordine contează în procesul de potrivire).

Exemplu:
Să se scrie o regulă care să afişeze toate microprocesoarele de 8 biţi, a căror putere este
strict mai mare decât 2 şi pentru care faţeta stare are valoarea depasit. Considerăm că BF conţine
fapte structurate de tip “Microprocesor”, conform definiţiei anterioare.

(defrule R1
(Microprocesor (nume $?n)
(bus 8)
(putere ?x&:(> ?x 2) )
(stare depasit) )
=>
(printout t ?n “este un microprocesor depasit” crlf) )

De observat cum tiparul din partea de condiţie a regulii de mai sus conţine atât perechea de
paranteze corespunzătoare faptului structurat, precum şi perechile de paranteze corespunzătoare
faţetelor care sunt folosite în crearea condiţiei respective. Nu este necesar ca într-un tipar dintr-o
regulă să apară toate faţetele unui fapt structurat, ci numai acele pentru care vrem să impunem
condiţii de potrivire. Ordinea faţetelor neavând importanţă, regula de mai sus este echivalentă cu
următoarea:

83
(defrule R2
(Microprocesor
(putere ?x&:(> ?x 2) )
(bus 8)
(stare depasit)
(nume $?n) )
=>
(printout t ?n “este un microprocesor depasit” crlf) )

În schimb, din punct de vedere a eficienţei cele două reguli de mai sus pot fi diferite (se va
ţine seama de principiile de construire eficientă a regulilor în CLIPS).
Nu există nicio restricţie privind tiparele referitoare la faptele structurate care pot fi folosite
la fel ca şi tiparele pentru faptele nestructurate (pot fi folosite şi împreună cu acestea) şi pot fi
folosite în condiţii privind ştergerea faptelor din BF, ca în exemplul următor.

(defrule R3
(faza actualizare BF)
(or ?a <- (Microprocesor
(bus ?b&:(< ?b 8) )
(stare depasit)
(nume $?n) )
?a <- (Microprocesor
(putere ?x&:(> ?x 5) )
(nume $?n) ) )
=>
(retract ?a)
(printout t ?n “este un microprocesor depasit sau de putere mare” crlf) )

Exerciţiu Experimentaţi funcţionarea regulii de mai sus, creând condiţiile pentru activarea
ei. Explicaţi comportarea.
Noi posibilităţi sunt create pentru faptele structurate prin folosirea comenzilor “duplicate” şi
“modify”.

Exemplu:

Să se scrie regula care să găsească o pereche de microprocesoare respectând condiţia ca


primul să aibă puterea mai mică sau egală cu 4, al doilea să aibă puterea mai mare decât 4,
ambele să fie în tehnologia CMOS şi să aibă starea depasit. Pentru primul, dacă faţeta viteza este
dată ca număr, aceasta se va modifica, trebuind să devină cu 1 mai mică, iar pentru al doilea se
va mai introduce în BF un fapt similar, dar cu faţeta tehnologie la valoarea NMOS şi cu puterea
având vechea valoare plus 2.

(defrule R4
?a <- (Microprocesor (tehnologie CMOS)
(stare depasit)
(viteza ?v)
(putere ?p1&:(<= ?p1 4) ) )

?b <- (Microprocesor (tehnologie CMOS)


(stare depasit)
(putere ?p2&:(> ?p2 4) ) )

84
=>
(if (numberp ?v) then (modify ?a (viteza =(- ?v 1) ) ) )
(duplicate ?b (tehnologie NMOS) (putere =(+ ?p2 2) ) ) )

Exerciţiu Experimentaţi funcţionarea regulii de mai sus, creând condiţiile pentru activarea
ei. Efectuaţi experimentul pentru ambele setări ale bistabilului privind faptele duplicat; explicaţi
ceea ce se întâmplă. Modificaţi regula în aşa fel încât să nu mai apară vreun efect negativ.

6. Verificarea automată a restricţiilor din faptele structurate

Unul din avantajele folosirii faptelor structurate, în comparaţie cu cele nestructurate, este
acela că sistemul efectuează în mod automat un control al corectitudinii definiţiei şi respectiv un
control al valorilor înscrise în faţete, ţinând seama de restricţiile impuse la definirea faptului
structurat. Există două variante de efectuare a acestei verificări: statică şi dinamică. Utilizatorul
poate seta tipul de verificare convenabil, fie folosind meniul Execution, opţiunea Options şi
fixând valoarea pentru bistabilele Constraint Checking, fie folosind comanda:

(set-dynamic-constraint-checking {TRUE,FALSE})

sau

(set-static-constraint-checking {TRUE,FALSE})

Fără a intra în toate detaliile, dacă verificarea este de tip static atunci ea se face numai la
încărcarea programelor, în timp ce pentru cazul verificării dinamice, aceasta se execută şi în
timpul rulării programelor. Pentru a explica modul de verificare sunt considerate următoarele
exemple ilustrative (se recomandă şi urmărirea lor experimentală).

Exemplul 1 Fie următoarea definiţie de fapt structurat:

(deftemplate Greseala (field abc (type FLOAT) (allowed-values 1 2 a) ) )

definiţie care este greşită prin aceea că determină două restricţii neconcordante în privinţa tipului
admis şi a valorilor admise pentru faţeta abc. Eroarea este semnalată chiar de la introducerea
definiţiei, aceasta nefiind acceptată.

Exemplu 2 Fie următoarea definiţie:

(deftemplate G1 (field mm (type INTEGER) (allowed-symbols A B) ) )

La introducerea ei se va semnala eroare privind neconcordanţa celor două restricţii folosite


şi definiţia nu este admisă.

Exemplul 3 Fie următoarea definiţie de fapt structurat:

(deftemplate fapt (field nume (type SYMBOL) ) (field val (allowed-values 1 2


3) ) )

La comanda (assert (fapt (nume 2))) se va semnala eroare datorită nerespectării condiţiei
de tip privind faţeta nume. Faptul nu este introdus în BF dacă este setată verificarea de tip static.
Aceasta se întâmplă şi atunci când faptul respectiv este încărcat dintr-un fişier. La comanda
(assert (fapt (val 5))) se semnalează eroare, cu aceeaşi explicaţie ca în cazul anterior.

85
O comportare diferită, funcţie de tipul setării, apare la comanda (assert (fapt) ). Dacă se
lucrează cu verificarea statică, atunci nu se semnalează nicio eroare; aceasta deşi valoarea care se
atribuie pentru faţeta val este nil, în conformitate cu regulile precizate anterior pentru fixarea
valorii unei faţete care nu are specificarea default. Valoarea astfel obţinută nu se încadrează în
valorile din lista “allowed-values” şi această eroare este semnalată dacă se lucrează cu
verificarea dinamică. Este de menţionat că şi pentru verificarea dinamică faptul este introdus în
BF, eroarea respectivă fiind numai semnalată, rămânând ca utilizatorul să ia măsurile necesare.

Exemplul 4 Dacă dintr-un fişier, sau de la nivel superficial se introduc următoarele două
definiţii:

(deftemplate A (field a (type INTEGER) (range 0 10) ) )


(defrule R5 (A (a 11) ) => )

atunci, indiferent de tipul setării (trebuie doar să fie setat unul din cele două tipuri de verificări
statică/dinamică), se semnalează eroare pentru regula R5, datorită tiparului regulii care nu
respectă cerinţa privind domeniul fixat pentru faţeta a; în consecinţă, regula R5 nu este luată în
considerare (nu este introdusă în BR).

Exemplul 5 Dacă sunt introduse următoarele două definiţii:

(deftemplate Simbol (field val (type LEXEME) ) )


(defrule R6 (Simbol (val ?x&:(> ?x 0) ) ) => )

La fel ca în exemplul anterior, dacă este setat un tip de verificare, se semnalează eroarea de
neconcordanţă a tipului valorii faţetei val şi a condiţiei de verificat în regula R6, astfel că regula
nu este introdusă în BR.
Exerciţiu Ştergeţi toate informaţiile din sistem (cu comanda “clear”), apoi reluaţi cele două
comenzi (“deftemplate” şi “defrule”) din exemplul anterior, în ordine inversată. Explicaţi
comportarea.

Exemplul 6 Fie următoarele două definiţii:

(deftemplate Fapt (field a1 (type STRING) ) (field a2 (type NUMBER) ) )


(defrule R7
(Fapt (a1 ?x) )
(Fapt (a2 ?x) )
=> )

Regula este greşită prin aceea că tiparele ei nu vor putea fi niciodată satisfăcute datorită
restricţiilor impuse pentru cele două faţete ale faptului structurat. Această eroare este semnalată
şi regula R7 nici nu este acceptată în BR, datorită erorii respective.

Exemplul 7 Dacă se introduc următoarele două definiţii:

(deftemplate Fapt1 (field a1 (type INTEGER) ) )


(defrule R8 => (assert (Fapt1 (a1 =(read) ) ) ) )

şi se activează regula R8, iar de la tastatură se introduce valoarea abc (adică un simbol), atunci
comportarea este diferită, în funcţie de setarea în care se lucrează. În cazul verificării statice nu
se semnalează nicio eroare; în cazul celei dinamice se semnalează eroarea privind neconcordanţa
tipului valorii furnizate pentru faţeta a1, faţă de tipul impus pentru faţetă prin definiţie; totuşi

86
faptul este introdus în BF, dar rularea se opreşte, pentru a avertiza utilizatorul. Este exemplul
care ilustrează deosebirea dintre comportarea statică şi cea dinamică.

7. Alte comenzi pentru faptele structurate; aspecte deosebite ale folosirii faptelor
structurate

Pentru manevrarea faptelor structurate există o serie de comenzi ajutătoare. Astfel,


“deftemplate” fiind o definiţie de construcţie, vor putea fi folosite comenzile “list-deftemplates”,
“ppdeftemplate” (semnificaţia şi sintaxa este aceeaşi ca la “list-deffacts” şi “ppdeffacts”) pentru
a lista toate definiţiile deftemplate existente, respectiv pentru a lista conţinutul unei anumite
definiţii “deftemplate”, sau se va putea folosi, pentru aceleaşi scopuri, meniul Browse.
O definiţie de fapt structurat se va putea şterge cu comanda: (undeftemplate <nume-
deftemplate>), după cum repetarea unei definiţii “deftemplate” cu acelaşi argument nume-
deftemplate conduce la înlocuirea vechii definiţii cu cea nouă, ca la orice altă definiţie de
construcţie. În plus, ştergerea şi redefinirea unui fapt structurat se pot face numai dacă faptul
respectiv nu este în funcţiune. Aceasta presupune să nu existe niciun fapt structurat cu numele
respectiv prezent în BF şi nicio regulă din BR să nu aibă în partea de condiţie sau în cea de
acţiune vreo referire la faptul structurat respectiv.

Exemplu Să presupunem că s-a introdus următoarea definiţie de fapt structurat:

(deftemplate F (field a) )

Dacă se dă apoi comanda (assert (F) ), atunci ştergerea definiţiei faptului structurat F
sau redefinirea acestuia se pot face numai după eliminarea din BF a faptului introdus prin
comanda “assert”.
De asemenea, dacă după definiţia de mai sus se definesc următoarele două reguli:

(defrule R9 (F) =>)


(defrule R10 => (assert (F) ) )

atunci redefinirea sau ştergerea definiţiei lui F nu mai este posibilă. Mai întâi trebuie şterse cele
două reguli şi abia apoi se va putea redefini F sau şterge definiţiei sa.
O situaţie deosebită este aceea când un acelaşi simbol CLIPS este folosit şi ca prim
câmp într-un fapt nestructurat şi ca argument nume-deftemplate într-un fapt structurat. O
asemenea situaţie ar putea fi generată prin următoarele comenzi:

(assert (F) ) ; se introduce in BF un fapt nestructurat.


(deftemplate F (field f1) (field f2) ) ; se defineşte şi un fapt structurat cu numele F.

Această situaţie determină o eroare şi definiţia faptului structurat cu numele F nu este


admisă, atâta timp cât în BF există un fapt nestructurat care are primul câmp F. Explicaţia este
aceea că de fapt în CLIPS faptele nestructurate sunt privite ca fapte structurate, având numele dat
de primul câmp şi o singură faţetă de tip multicâmp, implicită, care nu se defineşte explicit, şi
care conţine toate celelalte câmpuri ale faptului nestructurat. Rezultă atunci că în exemplul
prezentat mai sus este ca şi acum apar două definiţii contradictorii ale unui acelaşi fapt structurat,
cu numele F.

Exerciţii

1. Răspundeţi la următoarele întrebări:


• Care sunt deosebirile dintre faptele structurate şi cele nestructurate?

87
• Când va trebui preferată folosirea faptelor structurate?
• Care este valoarea default pentru următorul fapt structurat:

(deftemplate Aa (field b1) (field b2 (allowed-symbols as bas) )


(field b3 (allowed-values 1 3 2) ) (field b4 (type INTEGER) )
(field b5 (type STRING) (allowed-strings “lk” “1 2 3”) ) )

• Care sunt posibilităţile de introducere a faptelor structurate în BF?


• Câte fapte structurate vor fi în BF după comenzile:

(deftemplate A (field a) )
(assert (A) (A) (A (a 1) ) )

• Când va fi activată următoarea regulă ?

(defrule R11 (student (domiciliul Iasi) (nume $?n) )


(not (student (nume $?n) (domiciliul Vaslui) ) ) => )

2. Scrieţi un program CLIPS care folosind fapte structurate conţinând informaţii asupra
studenţilor unei grupe să determine localitatea unde îşi au domiciliul cei mai mulţi studenţi,
studentul care are media cea mai mare pentru semestrul curent (vor fi prezente informaţii cu
privire la notele din semestrul respectiv, media calculându-se numai pentru acei studenţi care au
cel mult o restanţă), numărul studenţilor integralişti, numărul studenţilor care nu au luat un
examen, al celor care nu au luat două examene, respectiv al celor care nu au luat mai mult de
două examene şi obiectul la care sunt cei mai mulţi restanţieri. Faptele structurate vor fi realizate
în aşa fel încât să se preîntâmpine introducerea de informaţii incorecte în BF (de exemplu, o notă
poate fi un număr întreg între 1 şi 10, numele disciplinelor de studiu sunt a priori cunoscute).
Indicaţie: Se va putea folosi pentru păstrarea informaţiilor cu privire la notele unui
student o faţetă multicâmp, în care să fie trecute, în perechi de câmpuri alăturate, numele
disciplinei şi nota obţinută, sau simbolul absent.

88
LUCRAREA NR. 8

Realizarea unui program de planificare a acţiunilor în CLIPS în varianta


nesimplificată
1. Enunţul problemei

Universul de discurs la care se referă problema este prezentat in Fig. 1. Acesta a mai fost
folosit şi într-un referat anterior, dar rezolvarea a fost una pentru un caz simplificat, în care
scopurile admise aveau o formă simplă. Reluând succint enunţul, sigurele elemente de interes
sunt corpurile (blocurile) sub formă de cuburi şi robotul care le poate mişca. Blocurile pot fi
plasate unul peste altul, pe podea, obţinându-se diferite configuraţii; pe podea pot fi plasate
oricâte blocuri. Programul CLIPS trebuie să găsească planul de mişcări pentru robot astfel încât
o configuraţia specificată prin starea finală să fie obţinută, cunoscându-se starea iniţială.
Programul trebuie să lucreze în cazul cel mai general, adică să găsească planul pentru robot
indiferent de configuraţiile luate în considerare în cele două stări (numărul de stive şi numărul de
blocuri).

D G D M

C K G L

B L F F A

A M E E B
Podea

Stare iniţială Stare finală


Fig. 1. O problemă de planificare în “lumea blocurilor”

2. Soluţia cea mai generală a problemei de planificare

Pentru a rezolva problema în cazul general, cel în care utilizatorul specifică doar
configuraţia iniţială şi cea finală a blocurilor, iar programul CLIPS găseşte secvenţa de mişcări
ale robotului, este necesară o reprezentare adecvată a stării iniţiale şi a celei finale. Se va urmări
ca în noua variantă de rezolvare să se păstreze soluţia dintr-un referat anterior (Lucrarea nr. 4) şi
aceasta să fie doar adaptată şi completată pentru a funcţiona şi în cazul general. Astfel, se va
păstra reprezentarea pentru starea iniţială, folosindu-se în acest sens fapte conform următorului
şablon pentru turnurile de blocuri care există în starea iniţială:

(stiva <<<bloc>>>)

iar pentru codificarea stării finale se propune următorul şablon al faptelor care reprezintă
turnurile de blocuri ce trebuie să existe în starea finală:

(scop_stiva <<<bloc>>>)

89
Astfel, construcţia “deffacts” necesară pentru cazul problemei din Fig. 1 este:

(deffacts Stare-initiala
(scop_stiva D G F E) (scop_stiva C K M L A B)
(stiva D C B A) (stiva F E) (stiva G K L M))

Pentru a păstra ca parte a rezolvării cele patru reguli din rezolvarea anterioară (“mutare-
directa”, “mutare-pe-podea”, “eliberare-sursa” şi “eliberare-destinatie”), trebuie concepute
câteva reguli care să convertească faptele existente în noua formă a programului în baza de fapte
(faptele “scop_stiva”) în fapte de tipul scopurilor simple, cele care puteau fi tratate de regulile
din forma anterioară. Prima dintre aceste reguli este:

(defrule Initiala1 (declare (salience 10))


(scop_stiva $? ?bl1 ?bl2 $?)
=> (assert (scop muta ?bl1 pe ?bl2)))

Această regulă creează scopurile elementare pentru perechile de blocuri care trebuie să
apară suprapuse în starea finală (evident, regula va fi activată şi executată de mai multe ori,
pentru toate perechile de blocuri suprapuse). Această regulă are ataşată o prioritate mai mare
deoarece este necesar ca rularea programului să înceapă cu regulile de iniţializare, cele care să
pregătească faptele necesare activării celorlalte reguli. Mai este necesară o asemenea regulă care
să fie dedicată blocurilor aflate în stive în starea finală pe podea.

Exerciţiu

Scrieţi regula de iniţializare care creează scopul elementar pentru blocurile ce trebuie să
fie într-o stivă în starea finală pe podea.

După rularea celor două reguli de iniţializare discutate mai sus se poate întâmpla ca în
baza de fapte să existe unele scopuri elementare redundante, produse de aceste reguli. Un
asemenea exemplu este dat de cazul blocurilor F şi E din Fig. 1; pentru aceste două blocuri nu
sunt necesare scopuri în baza de fapte deoarece acestea se găsesc deja în poziţia finală corectă,
chiar din starea iniţială. Pentru a îndepărta aceste fapte care reprezintă scopuri elementare
redundante, deja îndeplinite, sunt necesare două reguli specifice.

Exerciţiu

Scrieţi cele două reguli necesare eliminării scopurilor redundante care există în baza de
fapte după acţiunea regulilor de iniţializare. Ca indicaţie, trebuie manevrată cu atenţie o situaţie
precum aceea din Fig. 2 (acest caz este similar cu cel cunoscut în inteligenţa artificială sub
numele de “anomalia lui Sussman” după numele celui care a studiat mecanismele de planificare).

Un principiu pe care trebuie să-l urmărească programatorul în programarea bazată pe


reguli este şi acela de a elimina din baza de fapte orice fapt care nu mai este necesar. De
exemplu, în problema studiată, faptele de tip “scop_stiva ” nu mai sunt necesare după ce au
furnizat informaţiile necesare regulilor de iniţializare. De aceea, o regulă care va completa
programul pentru a elimina aceste fapte ce nu mai sunt necesare este:

(defrule Initiala3 "Regula de curatare a bazei de fapte"


(declare (salience 5))
?sc <- (scop_stiva $?)
=> (retract ?sc) )

90
B

B C

A C A podea

Stare iniţială Stare finală

Fig. 2. O problemă de tip “anomalia lui Sussman”

Pentru a aduce programul la forma finală mai trebuie făcute unele completări celor patru
reguli din vechea formă a soluţiei. Astfel, regulile “eliberare-sursa” şi “eliberare-destinatie” pot
determina în mod repetat un acelaşi scop, pentru că se poate întâmpla ca un acelaşi bloc să fie în
situaţia de a bloca atât un bloc sursă (un bloc care trebuie mutat undeva), cât şi mutarea către un
bloc destinaţie. Adaptarea se referă la adăugarea unui tipar de tip not, ajungându-se la
următoarea formă:

(defrule eliberare-sursa
(scop muta ?bloc1 pe ?)
(stiva ?top $? ?blocl $?)
(not (scop muta ?top pe podea))
=> (assert (scop muta ?top pe podea))
)

Exerciţiu

Scrieţi forma adaptată a regulii “eliberare-destinaţie”.

Mai multe ajustări necesită regula “mutare-directă”. Astfel, se poate observa felul în care
în timpul rulării programului două scopuri elementare distincte vor putea fi create pentru un
acelaşi bloc: un scop privind mutarea blocului în cauză peste alt bloc, conform situării finale
cerute şi respectiv un al doilea scop pentru mutarea sa pe podea, conform acţiunii uneia din
regulile “eliberare-sursa” sau “eliberare-destinatie”. Aceasta înseamnă că se poate întâmpla ca
pentru un anume bloc să fie activate la un moment dat atât regula “mutare-directa” cât şi regula
“mutare-pe-podea”. Evident, pentru un plan optim va fi de preferat regula “mutare-directa”
(mutarea unui bloc întâi pe podea şi apoi pe poziţia sa finală înseamnă o mişcare în plus, adică
ineficienţă). Acesta este motivul pentru care regulii “mutare-directa” i se va acorda o prioritate
mai mare.
În plus, rezultă că există două cazuri distincte în care regula “mutare-directa” se poate
activa şi executa: unul este cel în care pentru blocul pentru care se aplică regula “mutare-directa”
există în baza de fapte şi un scop privind mutarea sa pe podea, şi respectiv cazul când acest scop
nu există. Pentru aceste două situaţii diferite sunt necesare actualizări diferite ale bazei de fapte,
astfel că trebuie concepute două reguli distincte, derivate din forma anterioară a regulii “mutare-
directa”. Acestea sunt:

(defrule mutare-directa1 ” regula pentru cazul când nu exista si scopul de mutare


pe podea”
(declare (salience 1))
?sc <- (scop muta ?bl1 pe ?bl2)
(not (scop muta ?bl2 pe ?))

91
?st1 <-(stiva ?bl1 $?rest1)
?st2 <-(stiva ?bl2 $?rest2)
(not (scop muta ?bl1 pe podea))
=> (retract ?sc ?st1 ?st2) (assert (stiva ?bl1 ?bl2 ?rest2))
(if (neq ?rest1 (mv-append)) then (assert (stiva ?rest1)))
(printout t “Blocul ” ?bl1 “ este mutat pe ” ?bl2 crlf))
(defrule mutare-directa 2 ” regula pentru cazul când exista si scopul de mutare
pe podea”
(declare (salience 1))
?sc <- (scop muta ?bl1 pe ?bl2)
(not (scop muta ?bl2 pe ?))
?st1 <- (stiva ?bl1 $?rest1)
?st2 <- (stiva ?bl2 $?rest2)
?a <- (scop muta ?bl1 pe podea)
=> (retract ?a ?sc ?st1 ?st2) (assert (stiva ?bl1 ?bl2 ?rest2))
(if (neq ?rest1 (mv-append)) then (assert (stiva ?rest1)))
(printout t “Blocul ” ?bl1 “ este mutat pe ” ?bl2 crlf))

Apariţia în partea de condiţie a celor două reguli de mai sus a tiparului “(not (scop
muta ?bl2 pe ?))” are o explicaţie similară cu cea dată la regulile auxiliare care elimină
scopurile redundante, adică o explicaţie derivată din cazul “anomaliei lui Sussman”. Mai este de
remarcat felul în care aceste două reguli utilizează un element procedural în partea lor de acţiune,
adică o comandă “if”. O asemenea rezolvare – utilizarea unui “if” pentru un test simplu, poate
simplifica partea de condiţie a unei reguli. Oricum, o problemă care se pretează a fi abordată şi
este rezolvată prin programarea bazată pe reguli nu trebuie să conducă la o soluţie în care să
apară un exces de utilizări ale funcţiilor “if” şi “while” în părţile de acţiune ale regulilor (acestea
pot fi folosite numai în partea dreaptă a unei reguli). Dacă se întâmplă să avem o rezolvare în
CLIPS cu un exces de funcţii “if” şi “while”, atunci probabil rezolvarea bazată pe reguli nu este
soluţia cea mai eficientă.
În regulile anterioare funcţia “if” este utilizată pentru un test asupra variabilei ?rest1.
Dacă valoarea acesteia nu este vidă (adică valoarea multicâmp cu zero câmpuri, ceea ce se poate
obţine prin apelul funcţiei “mv-append” cu zero argumente), atunci înseamnă că blocul care este
mutat are un alt bloc sub el şi numai în acest caz este necesară o actualizare a bazei de fapte.

Exerciţiu

Încărcaţi într-un fişier soluţia completă a problemei de planificare în cazul general, cu


toate regulile şi cu o bază de fapte care să corespundă situaţiei din Fig. 1. Verificaţi funcţionarea
programului pe diferite strategii de rezolvare a conflictului.

92
Lucrarea nr. 9

Sistemul de menţinere a consistenţei bazei de fapte în CLIPS


Sistemul de menţinere a consistenţei (corectitudinii) bazei de cunoştinţe este o tehnică de
inteligenţă artificială care facilitează efectuarea raţionamentului nemonoton, adică a
raţionamentului necesar în cazul în care nu avem cunoştinţe complete asupra universului de
discurs, de-a lungul întregii evoluţii a acestuia. Raţionamentul monoton se referă la cazul în care,
având cunoştinţe complete asupra unei probleme, concluziile deduse sunt sigure şi nu vor fi
invalidate în timp. Atunci când se efectuează un raţionament de tip nemonoton o concluzie
dedusă poate fi invalidată ulterior şi va trebui eliminată din baza de cunoştinţe, împreună cu
celelalte elemente deduse cu ajutorul concluziei ce s-a dovedit incorectă, pentru a păstra
consistenţa bazei de cunoştinţe; rezultă că în acest caz baza de cunoştinţe îşi poate şi reduce
dimensiunea în anumite momente, de unde denumirea de raţionament nemonoton.
În CLIPS consistenţa BF se poate păstra prin folosirea unor tipare speciale în partea de
condiţie a regulilor, numite tipare “logical”. Sintaxa unui tipar “logical” este:

(logical <tipar>)

În partea de condiţie a unei reguli pot fi folosite oricâte tipare de tip “logical”, cu singura
restricţie ca acestea să fie plasate pe primele poziţii, putând fi urmate de tipare obişnuite. Efectul
folosirii tiparelor “logical” este acela că faptele care sunt introduse în BF prin partea de acţiune a
regulii respective devin susţinute logic de faptele care verifică tiparul sau tiparele de tip
“logical”. Aceasta înseamnă că atunci când faptul sau faptele de susţinere sunt şterse din BF şi
faptele susţinute sunt eliminate din BF, în mod automat.
Modul de funcţionare a susţinerii logice se va ilustra pe mai multe exemple.

Exemplul 1. Fie regula:

(defrule R1
(logical (a) )
(b)
=>
(assert (c) )
)

În partea de condiţie a regulii R există un tipar de tip “logical” şi un tipar obişnuit.


Efectul este următorul. Dacă în BF există faptele (a) şi (b) atunci regula R1 este activată şi, la
execuţie, ea determină introducerea în BF a faptului (c). Dacă ulterior din BF este şters faptul
(b), aceasta nu are nicio consecinţă asupra faptului (c); în schimb, la ştergerea din BF a faptului
(a), sistemul va elimina în mod automat şi faptul (c) din BF, deoarece acesta era susţinut (prin
tiparul “logical” al regulii R1) de către faptul (a).
Verificarea se poate face dacă, după introducerea regulii R1, se dau comenzile:

(assert (a) (b) )


(run)
(retract 1)

cu condiţia ca faptul (a) să fi intrat în BF la identificatorul f-1. Se va constata eliminarea


automată a faptului (c) din BF.

93
Exemplul 2. Următoarele două reguli sunt echivalente:

(defrule R2 (defrule R3
(logical (a1) (a2) ) (logical (a1) )
=> (logical (a2) )
(assert (A1) ) ) =>
(assert (A1) ) )

Exerciţiu Verificaţi funcţionarea susţinerii logice în cazul regulilor de mai sus.

Exemplul 3. Următoarea regulă este greşită sintactic:

(defrule R4
(a)
(logical (b) ) => )

deoarece are un tipar obişnuit plasat înaintea unui tipar “logical”.

Exemplul 4. Sub o condiţie “logical” poate fi plasată o operaţie SAU între mai multe
tipare, ca în cazul regulii R5

(defrule R5
(logical (or (b1) (b2) ) )
(b3) =>
(assert (B1) ) )

funcţionarea fiind cea obişnuită pentru condiţia “or” între tipare.

Exerciţiu Verificaţi funcţionarea regulii R5, observând felul în care se produce susţinerea
logică în acest caz. Scrieţi cele două reguli care să nu mai folosească condiţia “or” între tipare şi
care împreună să fie echivalente cu regula R5.

Exemplul 5. Un tipar de tip “not” poate fi plasat sub “logical”, caz în care se va produce
un efect de susţinere corespunzător. Fie în acest sens regula:

(defrule R6
(logical (a)
(not (b) ) )
=>
(assert (c) ) )

Dacă după introducerea acestei definiţii urmează comenzile:

(assert (a) )
(run)
(assert (b) )

vom constata că faptul (c), introdus în BF după execuţia regulii R6, este eliminat din BF, pentru
că prin introducerea lui (b) în BF partea de condiţie a regulii R6 nu mai este satisfăcută.

Exemplul 6. Un fapt poate fi în situaţia de a fi susţinut logic de diferite grupuri de fapte,


provenite din mai multe reguli. Într-un asemenea caz faptul respectiv va fi eliminat automat din

94
BF numai atunci când îşi pierde susţinerea din toate grupurile, aşa cum se ilustrează prin
următoarele două reguli:

(defrule R7 (defrule R8
(logical (a) ) (logical (e)
(b) (f) )
=> =>
(assert (c) (d) ) ) (assert (c) ) )

Exerciţiu Introduceţi regulile R7 şi R8 în BR, activaţi-le şi executaţi-le, iar apoi verificaţi


momentul când faptul (c), respectiv faptul (d), îşi pierd susţinerea logică. Explicaţi comportarea
obţinută.

Exemplul 7. În cazul în care se lucrează cu admiterea faptelor duplicat în BF (prin


trecerea pe TRUE a bistabilului “Fact Duplication”), se va ţine seama exact de faptele care au
determinat susţinerea logică; faptele identice din punct de vedere al conţinutului sunt deosebite
după indexul lor. Verificaţi acest lucru, trecând pe TRUE bistabilul “Fact Duplication” şi
folosind regula:

(defrule R9
(logical (P1) (P2) )
=>
(assert (P) ) )

Exerciţiu Introduceţi comenzile:

(assert (P1) (P2) (P1) (P1) (P2) )


(run)

după care eliminaţi din BF, pe rând, diferitele fapte (P1) şi (P2), observând care sunt faptele care
îşi pierd susţinerea logică.

Exemplul 8. Dacă un fapt este introdus în BF printr-o modalitate independentă de


susţinerea logică (aceasta poate fi o comandă “deffacts” urmată de “reset”, sau “assert” dată la
nivel superficial, sau din partea de acţiune a unei reguli), atunci susţinerea logică nu mai este
luată în considerare. Această situaţie este ilustrată prin următoarele două reguli:

(defrule R10 (defrule R11


(logical (y1) ) (y2)
=> =>
(assert (Y) ) ) (assert (Y) ) )

Observaţi ceea ce se întâmplă după comenzile următoare (în prealabil trebuie readus bistabilul
“Fact Duplication” pe FALSE, el fiind pe TRUE din exerciţiul anterior):

(assert (y1) (y2) )


(run)
(retract 1)

unde am presupus că faptul (y1) a fost introdus în BF la identificatorul f-1.

95
Exemplul 9. Dacă o regulă care a generat susţinerea logică pentru un fapt este ştearsă din
BR, atunci susţinerea logică respectivă este anulată. Aceasta înseamnă că faptul nu va mai fi
şters din BF la pierderea susţinerii logice, aşa cum se ilustrează în cazul următor.

(defrule R12
(logical (t1) )
=>
(assert (t2) ) )

La comenzile:

(assert (t1) )
(run)
(undefrule R12)
(retract 1)

dacă f-1 este identificatorul la care se află faptul (t1), vom constata că faptul (t2) nu este eliminat
din BF, susţinerea sa logică anulându-se odată cu ştergerea regulii R12 din BR.

Exemplul 10. O regulă în care există un tipar “logical” creează o dependenţă numai în
legătură cu comenzile “assert” existente în partea ei de acţiune, nu şi cu comenzile de ştergere a
faptelor din BF - “retract”. De exemplu, considerând regula:

(defrule R13
(logical (h1) )
?a <- (h2)
=>
(retract ?a) (assert (h3) ) )

la activarea şi execuţia ei faptul (h2) este scos din BF, iar (h3) este adăugat. La o scoatere
ulterioară din BF a lui (h1), faptul (h3) îşi pierde susţinerea logică şi este şters din BF, dar nu
trebuie să ne aşteptăm că se produce o readucere automată a faptului (h2) în BF.

Exemplul 11. Susţinerea logică se produce şi în legătură cu faptele structurate, ca în cazul


următor:

(deftemplate F (field f) )
(defrule R14
(logical (F (f 1) )
(fa) )
=>
(assert (Aa) ) )

Exerciţiu Verificaţi modul de manifestare a susţinerii logice în cazul regulii de mai sus.

Există o serie de comenzi ajutătoare pentru urmărirea susţinerii logice. Acestea sunt
următoarele.

(dependencies <index-fapt>)

Drept argument se poate folosi indexul unui fapt din BF sau o variabilă legată la indexul
unui fapt (un caz de folosire a unei variabile în acest scop este cel din exerciţiul 4, de la

96
exerciţiile recapitulative). Efectul este acela de listare a identificatorilor faptelor care asigură
susţinerea logică pentru faptul precizat prin argument.

Exemplu Fie următoarele două reguli:


(defrule R15 (defrule R16
(logical (Z1) ) (logical (Z2) (Z3) )
=> =>
(assert (Z4) ) ) (assert (Z4) ) )

Dacă în BF se introduc, începând de la identificatorul f-1, faptele (Z1), (Z2), (Z3) şi se dă


comanda (run), prin execuţia regulilor R15 şi R16 faptul (Z4) este introdus în BF, la
identificatorul f-4. Pentru a determina faptele care asigură susţinerea logică a lui (Z4) se va putea
folosi comanda:

(dependencies 4)

A doua comandă care poate fi folosită în legătură cu urmărirea susţinerii logice este:

(dependents <index-fapt>)

Argumentul poate fi indexul uni fapt din BF sau o variabilă legată la indexul unui fapt.
Rezultatul care se obţine este acela de listare a identificatorilor faptelor care primesc suportul
logic de la faptul indicat prin argument.

Exerciţiu Construiţi un exemplu în care să verificaţi modul de funcţionare pentru


comanda “dependents”.

Exerciţii recapitulative
1. Răspundeţi la următoarele întrebări.
• Ce înseamnă în inteligenţa artificială raţionament monoton şi respectiv nemonoton şi
care sunt cazurile în care trebuie să intervină sistemul de menţinere a consistenţei
(“truth maintenance system”) ?
• Ce se întâmplă atunci când un fapt este susţinut logic din mai multe reguli?
• Ce se întâmplă atunci când un tipar de tip “not” apare sub “logical”?
• Care este efectul comenzilor “dependencies” şi “dependents”?

2. Care este rezultatul ce se obţine după activarea şi execuţia regulii:

(defrule R17
(logical ?a <- (b) )
=>
(retract ?a)
(assert (c) ) )

3. Fie următoarea regulă:

(defrule R18
(logical ?a <- (A ?)
?b <- (A ?) )
(test (neq ?a ?b) )
=> (assert (c)))

97
Este regula respectivă corectă? Dacă da, când este regula activată, ce se întâmplă după
execuţia ei şi după eliminarea din BF a unuia din cele două fapte care au determinat activarea
(găsiţi răspunsurile pentru ambele situaţii ale bistabilului “Fact Duplication”).

4. Fie următoarele definiţii de construcţii (de observat că numele unui fapt structurat este
acelaşi cu numele unei reguli, fără să existe vreo restricţie în acest sens, sau vreo posibilitate de
confuzie):

(deftemplate M (field m) )

(defrule M
(logical ?a <- (M (m 1) )
(b) )
=>
(duplicate ?a (m 2) ) )

Cum se manifestă susţinerea logică în acest caz? Aceeaşi întrebare pentru cazul în care în
partea de acţiune a regulii M, acţiunea “duplicate” este înlocuită cu una “modify”, adică partea
dreaptă a regulii devine: (modify ?a (m 2) ) ).

5. Care este rezultatul care se obţine după efectuarea comenzilor (observaţi modul de
folosire a variabilei globale ?*A*) ?

(defglobal ?*A* = 0)
(defrule D1 (logical (d1) ) => (assert (d2) ) )
(defrule D2 ?a <- (d2) => (bind ?*A* ?a) )
(assert (d1) )
(run)
(dependencies ?*A*)

6. Refaceţi programul de planificare a acţiunilor unui robot care trebuie să mute blocuri
dintr-o configuraţie iniţială în una finală, folosind tipare de tip “logical”. În acest sens se va pleca
de la ultima forma a acestui program, în care au fost folosite două reguli muta-direct (date în
continuare), necesare pentru ca programul să furnizeze un răspuns corect indiferent de strategia
de rezolvare a conflictului care se foloseşte:

(defrule muta-direct1
(declare (salience 1) )
?sc <- (scop muta ?bl1 pe ?bl2)
(not (scop muta ?bl2 pe ?) )
?st1 <- (stiva ?bl1 $?rest1)
?st2 <-(stiva ?bl2 $?rest2)
(not (scop muta ?bl1 pe podea) )
=>
(retract ?sc ?st1 ?st2)
(assert (stiva ?bl1 ?bl2 $?rest2) )
(if (neq $?rest1 (mv-append) ) then (assert (stiva $?rest1) ) )
(printout t "blocul " ?bl1 " e mutat pe blocul " ?bl2 crlf) )

(defrule muta-direct2
(declare (salience 1) )
?sc <- (scop muta ?bl1 pe ?bl2)

98
(not (scop muta ?bl2 pe ?) )
?st1 <- (stiva ?bl1 $?rest1)
?st2 <- (stiva ?bl2 $?rest2)
?a <- (scop muta ?bl1 pe podea)
=>
(retract ?a ?sc ?st1 ?st2)
(assert (stiva ?bl1 ?bl2 $?rest2) )
(if (neq $?rest1 (mv-append) ) then (assert (stiva $?rest1) ) )
(printout t "blocul " ?bl1 " e mutat pe blocul " ?bl2 crlf) )

Se poate obţine o nouă soluţie, în care să se renunţe la regula muta-direct1, prin folosirea
tiparelor de tip “logical”. Obţineţi soluţia respectivă şi verificaţi funcţionarea pe diferitele
strategii de rezolvare a conflictului, observând influenţa acestora. Ca indicaţie, tiparele “logical”
vor fi folosite în regulile eliberare-sursa şi eliberare-destinatie.

99
Bibliografie

Brachman, R., Levesque, H. (2004). Knowledge representation and reasoning, Elsevier,


Amsterdam.

Giarratano, J., and Riley, G. (1989). Expert Systems: Principles and Programming, PWS-
KENT, Boston.

Jackson, P. (1999). Introduction to Expert Systems, Addison-Wesley, Harlow.

Kowalski, T., Levy, L. (1996). Rule-based programming, Kluwer, Boston.

Pănescu, D, A. (2000). Sisteme bazate pe cunoştinţe; reprezentarea cunoaşterii, Matrix


Rom, Bucureşti.

Riley, G. (2008). CLIPS Advanced Programming Guide, http://clipsrules.sourceforge.net/


OnlineDocs.html

Riley, G. (2008). CLIPS Basic Programming Guide, http://clipsrules.sourceforge.net/


OnlineDocs.html

100

S-ar putea să vă placă și