Sunteți pe pagina 1din 55

Mecanisme de soluţionare problemelor în

Prolog
Întrebări:
1. Unificarea
2. Backtracking
3. Recursivitate în Prolog
4. Controlul procesului de backtracking:
trace, cut si fail
5. Negaţia in Prolog
1. Unificarea
Unificarea se defineste ca fiind procesul de asociere
între o întrebare si o clauză.
Există 4 cazuri distincte de asociere:

1. Structurile identice se asociază evident între ele.


2. O asociere se poate face şi când apar una sau mai multe
variabile libere.
3. Două variabile libere se asociază între ele.
4. O variabilă anonimă (''_'') se asociază întotdeauna cu
orice.
Exemplu
DOMAINS
titlu, autor = symbol
pagini = unsigned
PREDICATES
carte(titlu, pagini)
scrisa_de(autor, titlu)
roman(titlu)
CLAUSES
scrisă_de(sadoveanu, „Neamul Soimarestilor”).
scrisă_de(creanga, „Amintiri din copilarie”).
carte(„Neamul Soimarestilor”, 300).
carte(„Amintiri din copilarie”, 100).
roman(Titlu) :- scrisa_de(_, Titlu) , carte(Titlu,
Lungime), Lungime >=250.
Exemplu
DOMAINS
titlu, autor = symbol ?- scrisa_de( X , Y).
pagini = unsigned % X şi Y variabile libere
PREDICATES X=sadoveanu, Y=Neamul
carte(titlu, pagini) Soimarestilor
nondeterm scrisa_de(autor, titlu) X=creanga, Y=Amintiri din
nondeterm roman(titlu) copilarie
CLAUSES 2 Solutions
scrisă_de(sadoveanu,„Neamul ___________________________
Soimarestilor”).
scrisă_de(creanga, „Amintiri din copilarie”).
carte(„Neamul Soimarestilor”, 300).
?- roman(T)
carte(„Amintiri din copilarie”, 100). T=„Neamul Soimarestilor”
roman(Titlu) :- scrisa_de(_, Titlu) , 1 solution
carte(Titlu, Lungime), Lungime >=250.
2. Backtracking

Backtracking este o metodă folosită de Prolog


pentru obţinerea unei soluţii (eventual mai multe
soluţii) pentru o anumită problemă şi reprezintă
mecanismul de cautare în baza de cunoştinţe.

Mecanismul acţioneaza iniţial pe setul de axiome


dupa care trece pe setul de reguli.

Mecanismul presupune cautare prin potrivire


dintre un predicat prezent în definiţia problemei şi
oclauza şi/sau o regulă.
Backtracking
Din momentul în care sistemul Prolog începe căutarea unei
soluţii pentru o întrebare dată, se poate ajunge în situaţia de
a decide între două sau mai multe posibile căi pe care să se
continue căutarea.

În acest caz se fixează un indicator(pointer) pentru acest


punct de ramificare (punct de backtracking) şi se selectează
prima cale pentru căutare.

Dacă se termină căutarea pe această variantă, se revine la


''punctul de backtracking'' si se reia căutarea unei solutii pe
următoarea cale disponibilă.
Cele patru principii de bază ale procesului de
backtracking sunt:
1. Sub-întrebările trebuie să fie satisfăcute (rezolvate) în
ordinea în care apar.

2. Clauzele sunt testate în ordinea în care apar în program.

3. Când o sub-întrebare se unifică cu capul unei reguli, corpul


acelei reguli este cel care urmează să fie satisfăcut.

4. O întrebare (scop) este satisfăcută când se găseste câte o


axiomă pentru procesul de unificare cu fiecare sub-
întrebare pozitionată la extremităţile arborelui de sub-
întrebări.
Algoritmul de căutare a solutiilor

Pentru a rezolva o sub-întrebare oarecare, sistemul


Prolog începe căutarea cu prima clauză din setul de
clauze care definesc predicatul ce apare în sub
întrebare.

In acest proces pot fi discutate două situatii


distincte :
Situaţia 1: se realizeza o unificare între
subîntrebare şi una din clauze,caz în care:
(a) - dacă mai există o altă clauză care ar putea să se unifice
cu aceeasi sub-întrebare, sistemul PROLOG fixează un
pointer la următoarea clauză, după cea pentru care s-a reusit
unificarea.

(b) - toate variabilele ''libere'' din sub-întrebare, care se asociază


cu valori din clauză devin variabile ''legate'' de valorile
corespunzătoare.

(c) - dacă clauza cu care s-a reusit unificarea subîntrebării este


capul unei reguli, urmează evaluarea corpului acelei reguli.
Termenii ce formează corpul devin sub-întrebări care trebuie
satisfăcute în ordinea în care apar.
Situaţia 2: nu se găseşte nici o clauză
care să se unifice cu sub întrebarea dată.

Sistemul PROLOG revine la ultimul pointer


de backtracking, eliberează de valori toate
variabilele care au fost ''legate'' după
momentul pozitionării acelui indicator de
backtracking, apoi încearcă să resătisfacă
subîntrebarea ce era activă în acel punct.
Exemplu de un program
Exemplu de funcţionare unui program
Exemplu:
PREDICATES
place(symbol,symbol)
gust(symbol,symbol)
mancare(symbol)
CLAUSES
place(paul, X) :- mancare(X) , gust(X, bun).
gust(pizza, bun).
gust(mere_verzi, rau).
mancare(mere_verzi).
mancare(pizza).

Să încercam un scop
goal : place(paul, Ce).
Prolog va afişa soluţia
Ce=pizza
1 Solution
3. Recursivitate în Prolog
Recursivitatea implică definirea unui predicat în funcţie
de el însuşi.
O definiţie recursivă trebuie să aibă întotdeauna cel
puţin două părţi:
1. condiţie elementară şi
2. o parte recursivă.

1 - defineşte un caz simplu, care ştim că este întotdeauna


adevărat.
2- simplifică problema, eliminând iniţial un anumit grad
de complexitate şi apoi apelându-se pe ea însăşi. La fiecare
nivel, condiţia elementară este verificată. Dacă s-a ajuns la
ea, recursivitatea se încheie, altfel, recursivitatea continuă.
Exemplu de apelare recursivă
discuta_despre(A,B) :- cunoaste(A,B).
discuta_despre(X,Y) :- cunoaste(X,Z) ,
discuta_despre(Z,Y).
Testaţi programul
4.Controlul procesului de backtracking:
trace,cut si fail
Prolog despune de un predicat standard incorporat
trace care permite :
• Afisarea valorilor variabililor instantiate la
fiecare pas de icercare a satisfacerii scopului

• Poate fi folosit pentru intelegere si depanare


programului.

Pentru aminarea acestui mod de rulare a


programului poate fi utilizat predicatul notrace .
Controlul procesului de backtracking:
trace, cut si fail

Modelul trace se manifesta prin patru evenimente:


• Call - apelarea se face la inceputul incercarii
satisfacerii unui scop
• Exit – la satisfacerea scopului
• Redo – prin backtrack se incerca resatisfacerea
scopului
• Fail - la cedarea scopului
Controlul procesului de backtracking:
trace, cut si fail

Pentru a controla fluxul de execuţie (modul de rulare) a


programului scris intr-un limbaj de programare logică
(Prolog), avem nevoie de a controla mechanizmul de
Backtracking.
In acest scop putem utiliza două instrumente:
• predicatul fail pentru forţarea procesului de
backtracking
• şi predicatul cut (!), pentru a impedica procesul de
backtracking.
Controlul procesului de backtracking:
cut

Procesul de resatisfacere prin backtracking poate fi


oprit de programator cu ajutorul predicatului cut ,
simbolizat prin caracterul “!”.

Predicatul cut (!), este un predicat standard, fără


argumente, care se îndeplineşte (este adevărat)
întotdeauna şi nu poate fi resătisfăcut.
Exemplu

Afiseaza(1) :- ! , write(‘unu’).
Afiseaza(2) :- ! , write(‘doi’).
Afiseaza(3) :- ! , write(‘trei’).
Afiseaza(4) :- ! , write(‘patru’).

goal : afiseaza(2)
goal: afiseaza
doi (2)
doi
yes
yes
Controlul procesului de backtracking:
cut

• Cînd într-o secvenţă de subscopuri se întilneste predicatul


cut (!), apelul la cut imediat este satisfăcut, şi se apelează
la următorul subscop ( dacă un astfel există)
• cut (!) - prima dată, cut reuşeşte
-cînd revin din backtracking la cut, acesta eşueaza
-toate regulile urmatoare al caror antet se unifica cu
antetul regulii cu cut – sunt ignorate!

Deci cut interzice satisfacerea altor scopurilor cu acelaşi


antet care urmează după regula cu cut şi astfel impedică
procesul de backtracking.
Controlul procesului de backtracking:
cut si fail
Comportarea predicatului cut este următoarea:
(C1) B :- C, D, ! , E, F.
(C2) B :- G, H.
(C3) B :- K, L.
Presupunem că scopul curent este B. Dacă C şi D sunt
satisfăcute, ele nu mai pot fi resatisfăcute datorită lui cut.
Dacă C şi D sunt satisfăcute, regulile C2 şi C3 nu vor mai fi
utilizate pentru resatisfacerea lui B. Resatisfacerea lui B se
poate face numai prin resatisfacerea unuia din scopurile E
şi F, dacă acestea au mai multe soluţii.
Exemplu de utilizare a predicatului cut
Exemplu :
debitor(popescu, ionescu). (1)

debitor(georgescu, ionescu). (2)

creditor(cristescu, popescu). (3)

creditor(vasilescu, ionescu). (4)

are_obligatii(X, Y) :-
debitor(Y, X). (5)

are_obligatii(X, Y) :-
creditor(X,Y). (6)
Exemplu 2:
Modificăm cele două reguli
anterioare, introducând cut

(5) are_obligatii(X, Y) :-
debitor(Y, X), !.
(6) are_obligatii(X, Y) :-
creditor(X, Y), !.

şi analizăm efectul său asupra


modului în care se va parcurge
arborele de căutare.
Rezultate

Pentru exemplu 1 : Pentru exemplu 2 :

X = ionescu, Y = popescu X = ionescu, Y = popescu


X = ionescu, Y =georgescu 1 Solution
X =cristescu, Y = popescu
X = vasilescu , Y = ionescu
4 Solutions
Utilizărea predicatului cut

•Predicatul de taiere cut (!) indica sistemului prolog


sa ignore anumite puncte de revenire

•Efectul este “taierea” unor ramuri din sptiu de


cautare:
- programele vor rula mai repede;
- programele vor ocupa mai putina memorie
(sunt mai putine puncte de revenire care
trebuiesc memorate)
Cut verde si Cut roşu
Există două contexte diferite în care se poate
utiliza predicatul cut:
• într-un context predicatul cut se introduce
numai pentru creşterea eficienţei programului,
caz în care el se numeşte “cut verde” ;

• în alt context utilizarea lui cut modifică


semnificaţia procedurală a programului, caz în
care el se numeşte „cut roşu” .
Cut verde si Cut roşu
Predicatul cut permite exprimarea în Prolog şi a
unor structuri de control de tipul:
dacă condiţie atunci acţiune1
altfel acţiune2

Astfel in Prolog:
daca_atunci_altfel (Cond, Ac1, Ac2) :- Cond, !, Ac1.
daca_atunci_altfel (Cond, Ac1, Ac2) :- Ac2.
Cut verde si cut roşu
Adăugarea lui cut verde nu schimba semnificaţia
procedurală , indiferent de ordinea în care se scriu clausele.
Utilizarea predicatului cut în definirea predicatului
asociat structurii de control daca_atunci_altfel introduce un
cut roşu deoarece efectul programului este total diferit dacă
se schimbă ordinea clauzelor.
Introducerea unui cut roşu modifică corespondenţa
dintre semnificaţia declarativă şi semnificaţia procedurală a
programelor Prolog. !!!
Exemplu de utilizare a predicatului cut
(verde şi roşu) pentru aflarea minimului
dintre două numere
Cut verde :
min_verde(X,Y,X) :- X <= Y , ! .
min_verde(X,Y,Y) :- X > Y , ! .
Daca schimbam cu locurile aceste doua predicate,sau
excludem ! – variabila X va obtine valoarea min din cele
doua X si Y. Dar ce se va intipmla daca vom aplica aceste
actiuni pentru fragmentul ce urmeaza ?
min_rosu(X,Y,X) :- X <= Y , !.
min_rosu(X,Y,Y).
Ce se intimpla in acest caz ?
Cut rosu
Dacă se schimbă ordinea clauzelor de definire a
predicatului min_roşu:
min2(X, Y, Y).
min2(X, Y, X) :- X =< Y, !.
atunci rezultatul programului va fi evident incorect
pentru valori X < Y.
Un rezultat incorect se va obtine si in cazul
ce urmeaza:
min2(X, Y, X) :- X =< Y.
min2(X, Y, Y). % s-a extras cut-ul
Utilizărea predicatului cut pentru
prelucrare listelor.
Aflarea elementului minim dintr-o listă cu elemente de tip
numeric: predicatul min(X,Y) este adevărat dacă şi numai
dacă Y este egal cu elementul minim din lista X.

min([X,Y],X) :- X <= Y, !.
min([X,Y],Y) :- X > Y, !.
min([H|X],Y) :- min(X,Y) , H >= Y, !.

În aceste exemple putem urmări şi apelarea recursivă la


coada listei, şi utilţizarea predicatului cut pentru a opri
“backtrackingul”. Care va fi efectul “!“ utilizat ?
Utilizărea predicatului cut pentru
prelucrare listelor.
Predicatul apartine(X,Y) este adevărat dacă X aparţine listei Y şi fals în
caz contrar. Un element X aparţine unei liste Y dacă lista începe cu X
sau dacă X aparţine restului listei.
apartine(X, [X| _ ] ). %1
apartine(X, [ _|Y] ) :- apartine(X,Y).
Daca introducem un scop:
apartine(a,[a,b,c,d,a,a,k,a])
Vom obţine mai multe soluţii:
YES
YES
YES
YES
No
Utilizărea predicatului cut pentru
prelucrare listelor. Exemple.

Dar pentru a confirma apartenenţa unui element la o


listă avem nevoie nu mai mult decat de un raspuns.
Acest scop se atinge prin utilizarea predicatului cut,
care evita procesul de revenire de mai multe ori

apartine(X,[X |_ ]) :- ! . %2
apartine(X,[ _ | Y ]) :- apartine(X,Y).
Utilizărea predicatului cut pentru
prelucrare listelor. Exemple.
Atunci pentru acelasi scop
apartine(a,[a,b,c,d,a,a,k,a])
se va obtine o singura solutie :
Yes
Introducerea predicatului cut in definiţia primei clauze a
predicatului apartine aduce la creşterea eficientei
programului. Odata ce s-a descoperit că un element este
membru al unei liste, este inutilă incercarea de resatisfacere
a scopului.
Utilizărea predicatului cut pentru
prelucrare listelor
Cu toate acestea, predicatul cut de mai sus nu este un cut verde,
deoarece el schimba comportarea programului în cazul în care se pun
intrebări în care X este variabila neinstantiată !
?- apartine(X, [a, b, c]).
X = a;
X = b;
X = c;
No
Se obtin trei solutii pentru cazul %1
?- apartine(X, [a, b, c]).
X=a
No
Si o o solutie pentru cazul %2.
Concluzii
Negatia Prolog
Eşuarea scopului poate fi exprimată în Prolog printr-un
predicat predefinit, care are valoarea de adevăr “fals”, şi
anume predicatul fail .
Predicatul fail este utilizat pentru forţarea revenirii pe
urme si este un predicat:
– standard,
– fără argumente,
– care eşuează întotdeauna.
Cu ajutorul predicatului fail se poate programa
negaţia dacă acest predicat se utilizează în
combinaţie cu predicatul ‘!’.
Negatia Prolog

Negaţia predicatului p(arg1, arg2,…, argn)


se exprimă prin not(p(arg1, arg2,…, argn)).
Predicatul
not(p(arg1, arg2,…, argn))

are valoarea logică “adevărat” dacă predicatul


p(arg1, arg2,…, argn) nu se poate satisface.
În caz contrar, are valoarea “ fals ” .
Legătura între not şi combinaţia taiere-eşec

Dacă P reuşeşte,conjuncţia subscopurilor !, fail face în primul


rînd (!) să fie abondanată încercarea de a satisface a doua
clauză şi în al doilea rînd (fail) provoacă un eşec al acestei
clauze, deci şi a predicatului în întregime. Adică, scopul
not(P) reuşeşte dacă şi numai dacă P nu poate fi satisfăcut.
Exemple
Fie avem un program ce constă dintr-o singură clauză
egal(X,X)
Să observăm răspunsurile la întrebările ce urmează.
?- not( egal(a,b) )
Yes
?- not( egal(a,a) )
No
?- egal(X,a) , not(egal(X,b))
X=a
?- not(egal(X,b)) , egal(X,a)
No
În ultima întrebare egal(X,b) reuşeşte şi unifică X cu b şi deci,
not(egal(X,b)) eşuează
Negatia Prolog

Să presupunem că dorim să exprimăm în


Prolog următoarea regulă:

“Daca cererea pentru un produs creşte şi producţia


nu creşte, atunci preţul produsului creste”.

Aceasta se poate realiza cu programul următor:


4. Negatia Prolog
domains
produs=symbol
predicates
creste_cerere(produs)
creste_productie(produs)
np(produs)
creste_pret(produs)
clauses
creste_cerere(mat_de_constructi).
creste_cerere(confectii).
creste_productie(confectii).
np(X):- creste_productie(X), ! , fail. %1
np(_). %2
creste_pret(X) :- creste_cerere(X) , np(X).
Răspunsurile la câteva interogări
...
clauses
creste_cerere(mat_de_constructi).
creste_cerere(confectii).
creste_productie(confectii).
np(X):- creste_productie(X), ! , fail. %1
np(_). %2
creste_pret(X):- creste_cerere(X) , np(X).
?- creste_pret(confectii)
No
?- creste_pret(mat_de_constructii)
Yes
?- creste_pret(X)
X = mat_de_constructii
1 Solution
…,!,fail == not(…)
domains
produs=symbol
predicates
creste_cerere(produs)
creste_productie(produs)
creste_pret(produs)
clauses
creste_cerere(mat_de_constructie).
creste_cerere(confectii). creste_productie(confectii).
creste_pret(X):-creste_cerere(X),
not (creste_productie(X)).
Forţarea backtrackingului cu ajutorul
predicatului fail
/* Program 1 */ goal: afis_clienti
domains popescu
nume = symbol Yes
predicates
client(nume)
afis_clienti
clauses
client(popescu).
client(ionescu).
client(vasilecu).
afis_clienti : -
client(X), write(X), nl.
Forţarea backtrackingului cu ajutorul
predicatului fail
/* Program 2 */
domains goal: afis_clienti
nume = symbol
predicates popescu
client(nume) ionescu
afis_clienti vasilesu
clauses No
client(popescu).
client(ionescu). Mesajul “No” de la sfârşit
este determinat de faptul că
client(vasilecu). scopul eşuează.
afis_clienti : -
client(X), write(X), nl, fail.
Putem evita obtinerea mesajului “No”
/* Program 3 */
domains
nume = symbol
predicates
client(nume)
afis_clienti
clauses
client(popescu).
client(ionescu).
client(vasilecu).
afis_clienti : - client(X), write(X), nl, fail.
afis_clenti.
Răspunsul la aceeaşi interogare este:
popescu
ionescu
vasilescu
Yes
Controlul procesului de backtracking
Predicatul de control Repeat
Predicatul repeat simuleaza structurile de control
repetitive din limbajele clasice de programare. El are
o infinitate de solutii, putandu-se resatisface de
oricate ori este nevoie, fara a umple stiva.
Definiţia lui ar putea fi:
repeat.
repeat :- repeat.
Se realizează cu succes întotdeauna. Furnizează un
număr infinit de puncte de alegere. Acest predicat este
predefinit in mediul SWI-Prolog.In alte compilatori
poate fi utilizat sub orice nume.
Predicatul de control Repeat
Exemplu:

double_char :–
repeat,
readchar(C) , /* citim variabila un simbol de la
testatura */
write(C,C), nl, /* afisam variabila C*/
C = ’.’ , ! , /* comparam simbolul citit cu ‘.’*/
nl , write („S-a introdus punct – am finisat !.") .
Tema urmatoare:
Reprezentarea cunoasterei in IA

SUCCES IN INVATARE !!!