• Nenhum resultado encontrado

Primii pa ş i într-un limbaj bazat pe reguli: CLIPS

7.1. Să facem o adunare

Comanda dată direct la prompter

Cum scriem în CLIPS că 2+1=3? OperaŃia de adunare se realizează prin operatorul de adunare +, scriind:

CLIPS> (+ 2 1)

urmat de un <Enter> (orice comandă se termină cu <Enter>). Interpretorul va întoarce:

3

după care vom avea din nou un prompter:

CLIPS>

ReŃinem aşadar că sintaxa comenzilor este în paranteze rotunde, cuvântul cheie semnificativ trebuind dat pe prima poziŃie.

Fapte

Să presupunem acum că dorim să introducem numerele pe care vrem să le adunăm ca date de intrare. Precizarea intrării într-un program CLIPS se face fie direct de la tastatură, fie citindu-le dintr-un fişier, fie prin declaraŃii de fapte. De exemplu, am putea avea două fapte ce conŃin numerele de adunat:

(numar 2) (numar 1)

Un fapt poate fi de două feluri: o construcŃie multi-câmp, în care câmpurile nu au nume şi ordinea lor este semnificativă – fapte ordonate, sau una în care câmpurile au nume şi, ca urmare, ordinea lor nu mai e importantă – obiecte. În acest capitol vom lucra numai cu fapte ordonate.

Primii paşi într-un limbaj bazat pe reguli: CLIPS 97

Un fapt este reprezentat cu unicitate în bază, acest lucru însemnând că sistemul va împiedica introducerea în bază a unui fapt identic cu unul deja existent.

Aceasta nu înseamnă că o regulă care ar încerca o astfel de acŃiune nu se aprinde.

Ea lucrează, dar fără să afecteze baza.

Dar cum facem ca interpretorul să ia cunoştinŃă de fapte? Cum la prompter el se aşteaptă să primească comenzi, încercarea de a le scrie direct acolo va eşua pentru că el nu cunoaşte nici o comandă numar:

CLIPS> (numar 2)

Missing function declaration for numar.

Comanda care comunică interpretorului un grup de fapte este deffacts:

Grupul de fapte şi numele lui nu au semnificaŃie pentru program. Dar el poate fi de folos programatorului din raŃiuni de structurare a datelor. Scriind la prompter acest rând, aparent nu se întâmplă nimic. Faptele sunt interpretate dar încă nu sunt depozitate în baza de fapte a sistemului. Interpretului trebuie să i se comande explicit ca faptele declarate prin definiŃiile de grupuri de fapte deffacts să fie depozitate în baza de fapte.

Comanda (reset) obligă includerea faptelor declarate în baza de fapte şi alimentează reŃeaua regulilor cu fapte, unul câte unul, în ordinea în care acestea sunt declarate şi care va fi şi ordinea de indexare a lor în bază. Verificarea conŃinutului bazei de fapte se face cu comanda (facts).

Deci, să reluăm secvenŃa:

CLIPS> (deffacts numere “numerele de adunat” (numar 2)(numar 1))

CLIPS> (reset) CLIPS> (facts) f-0 initial-fact f-1 (numar 2) f-2 (numar 1) CLIPS>

Observăm că, în afara faptelor pe care le-am depus noi, ne este raportat încă un fapt asupra căruia nu avem nici un merit: initial-fact. Utilitatea acestui fapt, pe care sistemul îl introduce întotdeauna, stă în filtrarea regulilor (deffacts numere “numerele de adunat” (numar 2)(numar 1))

un nume de grup de fapte

al doilea fapt un comentariu (opŃional) primul fapt

Programarea bazată pe reguli 98

fără parte stângă. Sistemul înlocuieşte partea stângă a regulilor care nu prevăd nici un şablon printr-un şablon initial-fact, ce va fi satisfăcut întotdeauna de acest fapt introdus din oficiu, făcând în acest fel posibilă şi aprinderea acestor reguli.

Acum avem faptele în bază. Cum procedăm pentru a aduna cele două numere? Va trebui să construim o regulă capabilă să preia numerele din cele două fapte şi să le adune. Această regulă va trebuie să fie atât de generală încât ori de câte ori conŃinutul acestor două fapte din bază s-ar modifica, ea să producă noua sumă.

Reguli

Pentru a defini o regulă folosim construcŃia defrule:

Un şablon este o construcŃie care “imită”, mai mult sau mai puŃin, un fapt ce ar trebuie să fie găsit în bază. In extremis, un şablon poate să fie identic cu un fapt, caz în care el se va potrivi numai cu acesta. Cel mai adesea însă un şablon permite anumite grade de libertate în structura faptelor cu care se intenŃionează să se potrivească. OperaŃia de căutare în bază a faptelor care se potrivesc cu un şablon o vom numi confruntare.

Un şablon pentru fapte ordonate este o construcŃie sintactică de genul:

şablon ::= (<element-şablon>*)

element-şablon ::= <atom> | ? | ?<var> | $? | $?<var>

ConstrucŃia de mai sus (dată într-un format Baccus-Naur) exprimă succint faptul că un şablon este format din zero sau mai multe elemente-şablon, iar un element-şablon poate fi un atom sau o construcŃie în care apare semnul ?. Un element-şablon atomic poate fi un întreg, un real, sau un simbol. Celelalte construcŃii se referă la câmpurile ce pot fi variabile în şablon. Confruntarea dintre un şablon şi un fapt înseamnă o parcurgere a şablonului în paralel cu faptul şi confruntarea fiecărui câmp din şablon cu unul sau mai multe câmpuri din fapt, în funcŃie de forma elementului-şablon. O regulă poate fi instanŃiată diferit în funcŃie de legările care se realizează între variabilele şabloanelor şi (defrule <nume-regulă> “comentariu” <şablon>* => <acŃiune>*)

orice regulă trebuie să aibă un nume

pot să scriu un comentariu care să descrie ce face regula

(opŃional)

partea stângă: un şir de şabloane

partea dreaptă: un şir de acŃiuni

Primii paşi într-un limbaj bazat pe reguli: CLIPS 99

câmpuri ale faptelor din bază. O instanŃiere eşuează ori de câte ori elementele şablonului se epuizează înainte de epuizarea câmpurilor faptului sau invers:

- dacă elementul-şablon e un atom, atunci operaŃia de confruntare reuşeşte dacă câmpul respectiv din fapt este identic cu elementul-şablon;

- dacă elementul-şablon e ?, atunci operaŃia reuşeşte;

- dacă elementul-şablon e ?<var>, unde <var> este un simbol, şi variabila ?<var> nu e legată, atunci operaŃia reuşeşte iar variabila ?<var>, ca efect colateral, va fi legată la valoarea din câmp;

- dacă elementul-şablon e ?<var>, şi variabila ?<var> e deja legată, atunci, dacă valoarea din câmp este identică cu cea la care este legată variabila, atunci operaŃia reuşeşte, altfel ea eşuează;

- dacă elementul-şablon e $?, atunci operaŃia reuşeşte şi un număr de instanŃieri ale regulii egal cu numărul câmpurilor rămase necercetate în fapt plus unu sunt produse. În fiecare instanŃiere confruntarea continuă de la o poziŃie diferită din fapt. Astfel, în prima instanŃiere confruntarea continuă din chiar poziŃia curentă a faptului, în a doua – de la o poziŃie situată un câmp mai la dreapta, ş.a.m.d.;

- dacă elementul-şablon e $?<var> şi variabila ?<var> nu e legată, atunci operaŃia reuşeşte şi au loc aceleaşi acŃiuni ca şi în cazul elementului- şablon $?. În plus, ca efect colateral, în fiecare instanŃiere variabila ?<var> va fi legată la o secvenŃă de câmpuri din fapt, cele peste care se face avansarea;

- dacă elementul-şablon e $?<var> şi variabila $?<var> e deja legată la o valoare multi-câmp, atunci, dacă această valoare multi-câmp concordă cu secvenŃa de câmpuri următoare din fapt, atunci operaŃia reuşeşte, altfel ea eşuează.

De remarcat din definiŃiile de mai sus că numele variabilei este ?<var> sau

$?<var>. Se interzice utilizarea în aceeaşi regulă a unui simbol de variabilă pentru a desemna simultan o variabilă simplu-câmp şi una multi-câmp.

Până acum am definit construcŃii ce pot apărea în partea stângă a unei reguli.

Ce acŃiuni pot să apară în partea dreaptă? Pentru moment, ieşirea programelor se va manifesta prin modificări asupra bazei de fapte. Putem modifica colecŃia de fapte din bază în două moduri: adăugând noi fapte sau retrăgând fapte existente acolo.

Ca să adăugăm, scriem: (assert <fact>*). Ca să retragem, trebuie însă să precizăm despre ce fapt e vorba. Nu se pune problema să încercăm identificarea din nou a unui fapt în partea dreaptă a regulii prin intermediul unui şablon: un şablon nu poate acŃiona decât în partea stângă. Ca urmare faptul ce trebuie retras trebuie să fi fost deja identificat printr-un şablon şi o adresă a lui să fi fost reŃinută. Ca să

reŃinem adresa unui şablon, ori indexul lui, folosim construcŃia:

?<var-idx> <- <şablon>

Programarea bazată pe reguli 100

care poate să apară doar în partea stângă a unei reguli. Dacă şablonul se potriveşte peste un fapt din bază, atunci variabila ?<var-idx> se va lega la indexul faptului. Cu aceasta, retragerea simultan a unui număr oarecare de fapte poate fi făcută printr-o comendă: (retract ?<var-idx>*).

Revenind la exemplul de adunare a celor două numere, o primă tentativă ar putea fi următoarea:

(defrule aduna (numar ?x) (numar ?y) => (assert (suma =(+

?x ?y))))

Faptul nou introdus cu comanda (assert ...) conŃine activarea unei evaluări (semnul = în faŃa apelului de funcŃie) ce utilizează operatorul de adunare. Cititorul va realiza că în aceeaşi manieră se poate forŃa o evaluare cu orice alt operator, ori de câte ori se doreşte ca valoarea unui câmp dintr-un fapt adăugat să rezulte dinamic.

Până acum programul nostru a constat dintr-o singură regulă. Pe aceasta am putut-o scrie direct la prompter. Dar când dimensiunea unui program e mai mare, maniera firească este să-l edităm într-un fişier şi, ca urmare, să-l încărcăm de acolo.

Comanda pentru încărcarea unui şir de declaraŃii dintr-un fişier este(load

<nume-fişier>).

Scriind o secvenŃă de declaraŃii defrule la prompter, sau comandând încărcarea lor dintr-un fişier, se construieşte baza de reguli, ceea ce revine la construcŃia reŃelei. Comanda (reset) forŃează apoi propagarea modificărilor- plus în reŃeaua de reguli prin alimentarea nodului rădăcină. Prin aceasta se realizează fazele de filtrare şi selecŃie. Lansarea efectivă în execuŃie a regulii selectate se realizează prin comanda (run). Această comandă amorsează procesul de inferenŃă.

În capitolele 3 şi 6 am arătat maniera în care se realizează legările variabilelor din părŃile stângi ale regulilor la valori, ce sunt instanŃele de regulă şi cum se calculează acestea. Bănuim, probabil, că nu e cazul să ne aşteptăm ca lansarea programului construit mai sus să producă o bază de fapte de genul:

CLIPS> (facts) f-0 initial-fact f-1 (numar 2) f-2 (numar 1) f-3 (suma 3) CLIPS>

Într-adevăr, afişarea bazei de fapte, cu comanda (facts), relevă existenŃa în bază a încă două fapte sumă:

Primii paşi într-un limbaj bazat pe reguli: CLIPS 101

f-0 initial-fact f-1 (numar 2) f-2 (numar 1) f-3 (suma 2) f-4 (suma 3) f-5 (suma 4)

Cum de au apărut acolo şi sumele 2 şi 4? Pentru a răspunde, să revedem mecanismul de aplicare a regulilor. Să presupunem că o regulă îşi satisface toate şabloanele asupra unei configuraŃii de fapte din bază. Spunem că s-a creat o activare a respectivei reguli. În acest caz, cel puŃin o instanŃă a regulii trebuie să apară în agendă. Aşa cum ştim, o instanŃă a unei reguli este formată dintr-un triplet ce asociază numelui regulii o secvenŃă de fapte, câte unul pentru fiecare şablon din partea de condiŃii a regulii, şi o configuraŃie de legări a variabilelor proprii la valori. Aceeaşi regulă, împreună cu o altă secvenŃă a unor fapte ce se potrivesc cu secvenŃa de şabloane a părŃii stângi, va genera o altă instanŃă. E posibil deci ca în instanŃe diferite să participe aceleaşi fapte dar într-o altă ordine.

Figura 27: InstanŃele regulii aduna

În exemplu nostru, în care apar regula aduna şi faptele iniŃiale (numar 2) şi (numar 1), partea de condiŃii a regulii, respectiv perechea de şabloane (numar

?x) şi (numar ?y), va crea instanŃele schiŃate în Figura 27.

Ca urmare, pentru fiecare dintre aceste instanŃe, presupunând că toate s-ar aplica, faptul calculat de partea dreaptă a regulii va fi:

aduna

(numar 2) inst1:

(numar 2)

inst2: (numar 2)

(numar 1)

inst3: (numar 1)

(numar 2)

inst4: (numar 1)

(numar 1)

regula şirul de fapte legările

?X2

?Y2

?X2

?Y1

?X1

?Y2

?X1

?Y1 aduna

aduna

aduna

Programarea bazată pe reguli 102

inst1: ?X = 2, ?y = 2 (suma 4) inst2: ?x = 2, ?y = 1 (suma 3)

inst3: ?x = 1, ?y = 2 (suma 3), deja existent inst4: ?x = 1, ?y = 1 (suma 1)

Un program care nu se mai termină

Modificând un singur simbol în regula aduna facem ca ea să se aplice la nesfârşit. Acest lucru se întâmplă, de exemplu, dacă în loc de fapte (suma …) am crea tot fapte (numar …):

(defrule aduna (numar ?x) (numar ?y)=> (assert (numar =(+

?x ?y))))

Bucla infinită e datorată faptului că la crearea fiecărui nou element din bază de forma (numar …), acesta va participa în atâtea activări câte fapte sunt deja în bază, ceea ce va duce la apariŃia a tot atâtea fapte noi, ş.a.m.d.

Dacă programul a intrat într-o buclă infinită, nu avem altă soluŃie decât să oprim programul prin mijloace brutale (<CTRL><C> sau <CTRL><ALT><DEL>, de exemplu).