Documente Academic
Documente Profesional
Documente Cultură
L1 (Îndrumar) PDF
L1 (Îndrumar) PDF
Autorii
3
Cuprins
Bibliografie ........................................………………………..….............100
4
LUCRAREA NR. 1
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.
Efectuare
Ieşiri Motor de inferenţe de
(expertiză) raţionamente
SE
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:
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:
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.
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
(+ 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.
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”.
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).
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:
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:
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:
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:
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.
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>>>)
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:
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:
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.
(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:
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
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:
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:
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.
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).
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.
În timpul evoluţiei unui SE apare frecvent ca necesară urmărirea agendei şi în acest sens
există în CLIPS comanda:
(agenda)
î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.
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ă?
(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)
• 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ă:
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”.
Î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)
(ppdeffacts <nume-deffacts>)
şi aceste comenzi fiind accesibile şi din meniul Browse, opţiunea Deffacts Manager.
Comenzile “defrule” şi “deffacts” pot fi şterse prin:
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:
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.
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):
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 ; ?
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:
î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:
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)
(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:
2. Transmiteţi comenzile:
(set-break unu)¶
(set-break doi)¶
(set-break trei)¶
(run)¶
(run)¶
(run)¶
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?
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
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
?<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:
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:
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:
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:
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.
(defrule R6
(este-bunic-al ?nume ?nume)
=>
(assert (are-nepot ?nume pe ?nume) ) )
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)
(defrule R7
(tatal-lui ?copil este ?tata)
(tatal-lui ?tata este ?bunic)
=>
(printout t “Bunicul lui ” ?copil “este” ?bunic crlf)
)
(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
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:
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.
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:
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:
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.
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) )
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.
Î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:
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:
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) )
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ă>>>)
Î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:
Î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:
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.
(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:
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))
(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?
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şă.
34
LUCRAREA NR. 4
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:
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
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.
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ă):
Î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:
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>)
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ă.
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:
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:
Î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.
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.
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)
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)¶
Exerciţiu
Scrieţi programul CLIPS care să determine toate permutările unei mulţimi cu trei elemente.
41
LUCRAREA NR. 5
• 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.
Î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:
Î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ă.
cu observaţia că înainte şi după simbolul | poate fi sau nu lăsat un spaţiu, deoarece | este el însuşi
un delimitator.
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:
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:
activează regula R8, cu variabilele ?x şi ?y legate la valorile 1401 şi respectiv 1402, în timp ce
faptele:
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:
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ă.
(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:
î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
(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)
(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:
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:
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.
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) )
(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))
Exerciţiu
Experimentaţi funcţionarea regulii R23, introducând în BF faptele:
(a 2 12) (b 10) (a abc 3).
(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)) => )
(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) )
(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.
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 ?
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>).
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.
(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).
(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?
(defrule R45
(a)
(b) =>
)
putem scrie:
(defrule R46
(and (a)
(b) ) => )
(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) )
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)
=>)
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.
(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.
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)
)
58
BF1:
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.
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:
(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:
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:
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
Exerciţii recapitulative
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.
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
În CLIPS se pot utiliza fişiere care trebuie în prealabil deschise, folosind funcţia:
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”:
sau
sau
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:
(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) ) )
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):
%-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:
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:
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.
(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:
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).
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>)
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>)
(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:
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.
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.
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>).
Î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>).
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).
Î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.
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).
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>)
(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>).
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ă.
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.
(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) ) ) )
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.
(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
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
(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.
(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
Î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ă.
Pentru a putea fi folosit un fapt de tip structurat trebuie mai întâi definit, prin comanda:
sau
sau
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ă>)
Exemplu:
Î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.
(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ă:
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) ) )
Specificarea de restricţionare de valori permise are sintaxa de una din următoarele forme:
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) ) )
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:
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:
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.
Exemplu:
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.
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 +∞.
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) ) )
Exemplu:
Având definiţia faptului structurat din exerciţiul anterior, exemple de introduceri în BF ale
unor fapte structurate cu numele Microprocesor sunt:
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:
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.
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:
(defrule R4
?a <- (Microprocesor (tehnologie CMOS)
(stare depasit)
(viteza ?v)
(putere ?p1&:(<= ?p1 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.
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ă).
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ă.
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:
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).
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.
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.
ş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
(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:
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:
Exerciţii
87
• Când va trebui preferată folosirea faptelor structurate?
• Care este valoarea default pentru următorul fapt structurat:
(deftemplate A (field a) )
(assert (A) (A) (A (a 1) ) )
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
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
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:
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).
90
B
B C
A C A podea
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
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:
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
92
Lucrarea nr. 9
(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.
(defrule R1
(logical (a) )
(b)
=>
(assert (c) )
)
93
Exemplul 2. Următoarele două reguli sunt echivalente:
(defrule R2 (defrule R3
(logical (a1) (a2) ) (logical (a1) )
=> (logical (a2) )
(assert (A1) ) ) =>
(assert (A1) ) )
(defrule R4
(a)
(logical (b) ) => )
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) ) )
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) ) )
(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ă.
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) ) )
(defrule R9
(logical (P1) (P2) )
=>
(assert (P) ) )
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ă.
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):
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.
(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.
(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ţ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”?
(defrule R17
(logical ?a <- (b) )
=>
(retract ?a)
(assert (c) ) )
(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
Giarratano, J., and Riley, G. (1989). Expert Systems: Principles and Programming, PWS-
KENT, Boston.
100