CATEGORII DOCUMENTE |
Mecanisme specifice
Controlul procesului de backtracking: cut si fail
Predicatul cut
Sistemul Prolog intra automat intr‑un proces de backtracking, daca acest lucru este necesar pentru satisfacerea unui scop. Acest comportament poate fi in unele situatii deosebit de util, dar poate deveni foarte ineficient in alte situatii. Se considera urmatorul exemplu de program, in care se definesc valorile unei functii:
f(X, 0) :- X < 3. % 1
f(X, 2) :- 3 =< X, X < 6. % 2
f(X, 4) :- 6 =< X. % 3
La intrebarea:
?- f(1, Y).
Y = 0
sistemul raspunde indicand ca valoarea functiei pentru X=1 este Y=0. Daca se pune intrebarea formata din conjunctia de scopuri:
?- f(1, Y), 2 < Y.
No
sistemul semnaleaza esec. Arborii de deductie corespunzatori acestor raspunsuri sunt prezentati in figura 1.
Se observa ca se incearca resatisfacerea primului scop cu regulile 2 si 3, desi acest lucru este evident inutil, datorita semanticii acestor reguli. Cel care a scris aceste reguli poate cu usurinta constata ca, daca o valoare mai mica decat 3 pentru X duce la esecul scopului S din conjunctia de scopuri f(X, Y), S, este inutil sa se incerce resatisfacerea scopului f, deoarece aceasta nu este posibila. Se poate impiedica incercarea de resatisfacere a scopului f(X, Y) prin introducerea predicatului cut.
|
Figura 1. Arborii de deductie a scopurilor f(1,Y) si f(1,Y),2 < Y
Predicatul cut, notat cu atomul special !, este un predicat standard, fara argumente, care se indeplineste (este adevarat) intotdeauna si nu poate fi resatisfacut.
Predicatul cut are urmatoarele efecte laterale:
La intalnirea predicatului cut, toate selectiile facute intre scopul antet de regula si cut sunt 'inghetate', deci marcajele de satisfacere a scopurilor sunt eliminate, ceea ce duce la eliminarea oricaror altor solutii alternative pe aceasta portiune. O incercare de resatisfacere a unui scop, intre scopul antet de regula si scopul curent va esua.
Daca clauza in care s‑a introdus predicatul cut reuseste, toate clauzele cu acelasi antet cu aceasta, care urmeaza clauzei in care a aparut cut, vor fi ignorate. Ele nu se mai folosesc in incercarea de resatisfacere a scopului din antetul clauzei care contine cut.
Pe scurt, comportarea predicatului cut este urmatoarea:
(C1) H :- D1, D2, ., Dm, !, Dm+1, ., Dn.
(C2) H :- A1, ., Ap.
(C3) H.
Daca D1, D2, ., Dm sunt satisfacute, ele nu mai pot fi resatisfacute datorita lui cut. Daca D1, ., Dm sunt satisfacute, C2 si C3 nu vor mai fi utilizate pentru resatisfacerea lui H. Resatisfacerea lui H se poate face numai prin resatisfacerea unuia din scopurile Dm+1, ., Dn, daca acestea au mai multe solutii.
Utilizand predicatul cut, definitia functiei f(X, Y) poate fi rescrisa mult mai eficient astfel:
f(X, 0) :- X < 3, !.
f(X, 2) :- 3 =< X, X < 6, !.
f(X, 4) :- 6 =< X.
Predicatul cut poate fi util in cazul in care se doreste eliminarea unor pasi din deductie, care nu contin solutii sau eliminarea unor cai de cautare care nu contin solutii. El permite exprimarea in Prolog a unor structuri de control de tipul:
daca conditie atunci actiune1
altfel actiune2
astfel:
daca_atunci_altfel(Cond, Act1, Act2) :- Cond, !, Act1.
daca_atunci_altfel(Cond, Act1, Act2) :- Act2.
Se observa insa ca exista doua contexte diferite in care se poate utiliza predicatul cut: intr‑un context predicatul cut se introduce numai pentru cresterea eficientei programului, caz in care el se numeste cut verde; in alt context utilizarea lui cut modifica semnificatia procedurala a programului, caz in care el se numeste cut rosu.
Exemplul de definire a functiei f(X, Y) cu ajutorul lui cut este un exemplu de cut verde. Adaugarea lui cut nu face decat sa creasca eficienta programului, dar semnificatia procedurala este aceeasi, indiferent de ordinea in care se scriu cele trei clauze.
Utilizarea predicatului cut in definirea predicatului asociat structurii de control daca_atunci_altfel introduce un cut rosu, deoarece efectul programului este total diferit daca se schimba ordinea clauzelor. Introducerea unui cut rosu modifica corespondenta dintre semnificatia declarativa si semnificatia procedurala a programelor Prolog.
Se considera exemplul de definire a predicatului de aflare a minimului dintre doua numere, in urmatoarele doua variante:
min1(X, Y, X) :- X =< Y, !. % cut verde
min1(X, Y, Y) :- X > Y.
min2(X, Y, X) :- X =< Y, !. % cut rosu
min2(X, Y, Y).
In definitia predicatului min1 se utilizeaza un cut verde; el este pus pentru cresterea eficientei programului, dar ordinea clauzelor de definire a lui min1 poate fi schimbata fara nici un efect asupra rezultatului programului. In cazul predicatului min2 se utilizeaza un cut rosu, asemanator structurii daca_atunci_altfel. Daca se schimba ordinea clauzelor de definire a predicatului min2:
min2(X, Y, Y).
min2(X, Y, X) :- X =< Y, !.
rezultatul programului va fi evident incorect pentru valori X < Y.
In anumite cazuri, efectul introducerii predicatului cut poate fi mai subtil. Se considera din nou definitia predicatului membru:
membru(X, [X | _]).
membru(X, [ _ |Y]) :- membru(X, Y).
si o definitie alternativa:
membru1(X, [X| _]) :- !.
membru1(X, [ _ |Y]) :- membru1(X, Y).
Introducerea predicatului cut in definitia primei clauze a predicatului membru1 este justificata datorita cresterii eficientei programului. Odata ce s‑a descoperit ca un element este membru al unei liste, este inutila incercarea de resatisfacere a scopului. Cu toate acestea, predicatul cut de mai sus nu este un cut verde, deoarece el schimba comportarea programului in cazul in care se pun intrebari in care X este variabila neinstantiata in predicatele membru si membru1.
?- membru(X, [a, b, c]).
X = a
X = b
X = c
No
trei solutii pentru membru
?- membru1(X, [a, b, c]).
X = a
No
o solutie pentru membru1.
Efectul introducerii predicatului cut asupra semnificatiei declarative a programelor Prolog poate fi rezumat astfel:
p :- a, b. % Semnificatia declarativa este:
p :- c. % (a b) c
% indiferent de ordinea clauzelor (in general).
p :- a, !, b. % Semnificatia declarativa este:
p :- c. % (a b) (~ a c).
p :- c. % Semnificatia declarativa este:
p :- a, !, b. % c (a b).
Oportunitatea utilizarii unui cut rosu sau a unui cut verde este, dintr‑un anumit punct de vedere, similara cu cea a utilizarii sau nu a salturilor in limbajele de programare clasica. Daca s‑ar rescrie predicatul daca_atunci_altfel folosind un cut verde, definitia lui ar fi:
daca_atunci_altfel(Cond, Act1, Act2) :- Cond, !, Act1.
daca_atunci_altfel(Cond, Act1, Act2) :- not(Cond), !, Act2.
unde predicatul not(Cond) este satisfacut, daca scopul Cond nu este satisfacut. O astfel de definitie implica evaluarea lui Cond de doua ori si, daca Cond se defineste ca o conjunctie de scopuri, posibil sofisticate, atunci ineficienta utilizarii unui cut verde in acest caz este evidenta. De la caz la caz, programatorul in Prolog trebuie sa aleaga intre claritate, deci pastrarea corespondentei semnificatiei declarative cu cea procedurala, si eficienta.
Consideram urmatorul exemplu:
C :- P, Q, R, !, S, T, U.
C :- V.
A :- B, C, D.
A.
Executia scopului C va fi afectata de cut astfel: procesul de backtracking va fi posibil pe lista de scopuri P, Q, R; totusi, indata ce cut‑ul este atins, toate solutiile alternative ale listei de scopuri P, Q, R sunt eliminate. Clauza alternativa despre C:
C :- V.
va fi si ea ignorata. Procesul de acktracking va fi totusi inca posibil pe lista de scopuri S, T, U. Scopul parinte al clauzei care contine cut‑ul este scopul C din clauza:
A :- B, C, D.
Prin urmare cut‑ul va afecta doar executia scopului C. Pe de alta parte, el nu va fi vizibil in scopul A. Astfel, procesul de backtracking in cadrul listei de scopuri B, C, D va ramane activ, indiferent de cut‑ul din clauza folosita pentru a satisface scopul C.
Predicatul fail
In multe situatii intereseaza atat conditiile de satisfacere a scopurilor, cat si conditiile de nesatisfacere a acestora, deci de esec. Fie urmatoarele fapte Prolog:
bun(gelu).
bun(vlad).
bun(mihai).
Pe baza acestor fapte se poate obtine un raspuns afirmativ despre bunatatea lui Gelu, Vlad si Mihai si un raspuns negativ daca se intreaba despre bunatatea oricarei alte persoane. Din acest exemplu se poate vedea ca limbajul Prolog foloseste un model de rationament minimal numit ipoteza lumii inchise. Conform acestui model, tot ceea ce nu este stiut de program, deci afirmat explicit ca adevarat in program, este considerat fals.
Limbajul Prolog permite exprimarea directa a esecului unui scop cu ajutorul predicatului fail. Predicatul fail este un predicat standard, fara argumente, care esueaza intotdeauna. Datorita acestui lucru, introducerea unui predicat fail intr‑o conjunctie de scopuri, de obicei la sfarsit, caci dupa fail tot nu se mai poate satisface nici un scop, determina intrarea in procesul de backtracking.
Daca fail se intalneste dupa predicatul cut, nu se mai face backtracking. Enuntul 'Un individ este rau daca nu este bun.' se poate exprima astfel:
bun(gelu).
bun(vlad).
bun(mihai).
rau(X) :- bun(X), !, fail.
rau(X).
?- rau(gelu).
No
?- rau(mihai).
No
?- rau(petru).
Yes
Daca predicatul fail este folosit pentru a determina esecul, cum este cazul exemplului de mai sus, este de obicei precedat de predicatul cut, deoarece procesul de backtracking pe scopurile care il preced este inutil, scopul esuand oricum datorita lui fail.
Exista cazuri in care predicatul fail este introdus intentionat pentru a genera procesul de backtracking pe scopurile care il preced, proces interesant nu din punctul de vedere al posibilitatii de resatisfacere a scopului ce contine fail, ci din punctul de vedere al efectului lateral al acestuia.
rosu(mar).
rosu(cub).
rosu(soare).
afisare(X) :- rosu(X), write(X), nl, fail.
afisare( _ ).
Scopul afisare(X) va afisa toate obiectele rosii cunoscute de programul Prolog datorita procesului de backtracking generat de fail; in acest fel s-a relizat prin fail o iteratie peste faptele rosu( ). Clauza afisare( _ ) este adaugata pentru ca raspunsul final la satisfacerea scopului sa fie afirmativ. In acest caz, introducerea unui cut inainte de fail ar fi determinat numai afisarea primului obiect rosu din program.
Revenind la combinatia !,fail, se considera in continuare implementarea in Prolog a afirmatiei: 'Mihai iubeste toate sporturile cu exceptia boxului'. Aceasta afirmatie poate fi exprimata in pseudocod sub forma:
daca X este sport si X este box
atunci Mihai iubeste X este fals
altfel daca X este sport
atunci Mihai iubeste X este adevarat
si tradusa in Prolog astfel:
iubeste(mihai, X) :- sport(X), box(X), !, fail.
iubeste(mihai, X) :- sport(X).
Cele doua clauze, de mai sus, pot fi compactate intr‑una singura astfel:
iubeste(mihai, X) :- sport(X), box(X), !, fail ; sport(X).
Predicatul cut utilizat aici este un cut rosu. Combinatia !, fail este deseori utilizata in Prolog si are rolul de negatie. Se mai spune ca limbajul Prolog modeleaza negatia ca esec al satisfacerii unui scop (negatia ca insucces), aceasta fiind de fapt o particularizare a ipotezei lumii inchise. Combinatia !, fail este echivalenta cu un predicat standard existent in Prolog, predicatul not. Predicatul not admite ca argument un predicat Prolog si reuseste daca predicatul argument esueaza. Utilizand acest predicat, ultimul exemplu dat se poate exprima in Prolog astfel:
iubeste(mihai, X) :- sport(X), not(box(X)).
Un alt predicat standard este predicatul call, care admite ca argument un predicat Prolog si are ca efect incercarea de satisfacere a predicatului argument. Predicatul call reuseste daca predicatul argument reuseste si esueaza in caz contrar. Utilizand acest predicat, se poate explicita efectul general al predicatului standard not in urmatorul mod:
not(P) :- call(P), !, fail.
not(P).
Atat predicatul not, cat si predicatul call sunt predicate de ordinul II in Prolog, deoarece admit ca argumente alte predicate.
Ca o aplicatie la cele discutate pana acum, iata cateva exemple:
Ex1. Am definit predicatul care calculeaza maximul dintre doua numere (in prima parte) astfel:
% max(+X, +Y, -Max).
max(X, Y, X) :- X >= Y.
max(X, Y, Y) :- X < Y.
Cele doua reguli sunt mutual exclusive. Daca prima reuseste atunci a doua va esua. Daca prima esueaza, atunci a doua trebuie sa reuseasca. Prin urmare, o reformulare mai eficienta este posibila:
daca X Y atunci Max = X
altfel Max = Y.
Aceasta se scrie in Prolog utilizand cut astfel:
max(X, Y, X) :- X >= Y, !.
max( _ , Y, Y).
Sa vedem ce se intampla insa atunci cand dorim sa folosim puterea generativa a limbajului Prolog:
% max(?X, ?Y, ?Max) max(X, Y, X) :- X >= Y. max(X, Y, Y) :- X < Y. |
% max(?X, ?Y, ?Max) max(X, Y, X) :- X >= Y, !. max( _ , Y, Y). |
|
max(X, 3, 4). |
X = 4 |
X = 4 |
max(3, X, 4). |
X = 4 |
X = 4 |
max(X, X, 3). |
X = 3 |
X = 3 |
Cazuri in care predicatul max trebuie sa esueze: |
||
max(X, 3, 2). |
No |
No |
max(3, X, 2). |
No |
X = 2 |
Din exemplele prezentate se observa ca varianta mai "explicita" se comporta corect in toate cazurile, in timp ce varianta "eficienta" genereaza rezultate eronate. De aceea, inainte de a utiliza cut, trebuie sa ne gandim in ce context si cum vor fi apelate predicatele in care el apare, deoarece adesea folosirea lui cut creste posibilitatea aparitiei erorilor de programare.
Ex2. Plasarea a opt regine pe o tabla de sah astfel incat ele sa nu se atace intre ele:
% solutie(?Solutie)
solutie([]).
solutie([X/Y | CelelalteRegine]) :-
solutie(CelelalteRegine),
membru(Y, [1, 2, 3, 4, 5, 6, 7, 8]),
not(ataca(X/Y, CelelalteRegine)).
% ataca(+Regina, +CelelalteRegine) - verifica daca Regina ataca CelelalteRegine
ataca(X/Y, CelelalteRegine) :-
membru(X1/Y1, CelelalteRegine),
(Y1 = Y; Y1 is Y + X1 - X; Y1 is Y - X1 + X).
% membru(?X, ?Lista) - membru generativ
membru(X, [X| _ ]).
membru(X, [ _ |L]) :- membru(X, L).
% model de solutie
model([1/Y1, 2/Y2, 3/Y3, 4/Y4, 5/Y5, 6/Y6, 7/Y7, 8/Y8]).
% toate solutiile
toate :- model(L), assert(count(1)), !,
( solutie(L), retract(count(N)), N1 is N + 1, assert(count(N1)),
write(N), write('.'),write(L), nl, fail ; retract(count( _ )), true).
Cut‑ul a fost introdus doar pentru eficienta (cut verde), iar parantezele care inchid disjunctia de conjunctii de scopuri au fost puse din cauza lui.
Ex3. Sa scriem predicatul unifica(X, Y) care reuseste daca X si Y unifica, fara insa a modifica pe X sau pe Y. Folosind predicatul not, putem scrie:
unifica(X, Y) :- not(not(X = Y)).
Daca X si Y unifica, adica X = Y reuseste, atunci not(X = Y) esueaza, unificarea fiind desfacuta, si not(not(X = Y)) reuseste (deci unifica(X, Y) reuseste), X si Y ramanand neunificate.
Predicate predefinite
Predicatele prezentate pana acum, cu exceptia predicatului call, sunt predicate bazate pe logica cu predicate de ordinul I, modelul Prolog cu semnificatia declarativa a programelor urmarind indeaproape modelul logic. In continuare se prezinta o serie de predicate Prolog care nu mai pot fi asimilate cu logica cu predicate de ordinul I, respectiv: predicate ce decid asupra caracterului unor argumente, predicate de control a fluxului de executie a programului si predicate de ordinul II, numite uneori si metapredicate. Metapredicatele accepta ca argumente alte predicate Prolog, fapte sau reguli, si sunt interesante in special prin efectul lateral pe care il produc. Toate tipurile de predicate mentionate reprezinta extinderea modelului programarii logice cu tehnici de programare care cresc puterea de calcul a limbajului. Majoritatea predicatelor prezentate in continuare sunt standard si se gasesc predefinite in orice implementare, o parte sunt specifice mediului SWI-Prolog, acestea fiind indicate ca atare. O implementare particulara poate contine si alte predicate predefinite suplimentare celor prezentate aici.
Predicate de clasificare a termenilor
var(X)
Predicatul var(X) reuseste daca X este o variabila neinstantiata si esueaza in cazul in care X este o variabila instantiata sau o constanta.
Exemple
?- var(5).
No
?- var(mihai).
No
?- var(X).
Yes
nonvar(X)
Predicatul nonvar(X) este adevarat daca X nu este o variabila neinstantiata. Acest predicat este opusul prdicatului var(X) si s‑ar putea defini astfel:
nonvar(X) :- var(X), !, fail.
nonvar( _ ).
atom(X)
Predicatul atom(X) reuseste daca X este un atom Prolog si esueaza in caz contrar.
Exemple
?- atom(coco).
Yes
?- atom(100).
No
?- atom(carte(prolog, clocksin)).
No
integer(X)
Predicatul integer(X) reuseste daca X este un numar intreg.
Se considera urmatorul exemplu de program Prolog, care transforma o expresie reprezentata printr‑o suma de variabile si constante intr‑o expresie care contine suma variabilelor plus o constanta, care reprezinta suma tuturor constantelor din expresie. Predicatul simplif are doua argumente: primul argument este forma initiala a expresiei, argument instantiat, iar cel de al doilea reprezinta expresia simplificata, sintetizata de program.
% simplif(Expresie_initiala, Expresie_simplificata)
% Predicat ajutator simpl
% simpl(Ex_initiala, Suma_acumulata, Ex_simplificata).
simplif(Expr, Expr1) :- simpl(Expr, 0, Expr1).
simpl(Expr + A, N, Expr1) :-
integer(A), !,
N1 is N + A,
simpl(Expr, N1, Expr1).
simpl(Expr + A, N, Expr1 + A) :-
atom(A), !,
simpl(Expr, N, Expr1).
simpl(Rest, N, N1) :-
integer(Rest), !,
N1 is Rest + N.
simpl(Rest, 0, Rest) :- !.
simpl(Rest, N, N + Rest).
La intrebarea
?- simplif(x + 40 + y + 1 + 55 + x + 2, E).
programul raspunde
E = 98 + x + y + x
In programul de mai sus, definirea predicatului simplif s‑a facut pe baza unui predicat ajutator simpl, care contine un argument suplimentar. Acest argument, instantiat la 0 in cazul primului apel, este folosit ca o variabila de acumulare pentru calculul sumei constantelor intregi din expresie.
atomic(X)
Pe baza predicatelor atom(X) si integer(X) se poate defini predicatul atomic(X), care este adevarat daca X este fie intreg, fie atom Prolog:
atomic(X) :- atom(X).
atomic(X) :- integer(X).
De multe ori predicatul atomic(X) este predefinit in sistem.
Predicate de control
Predicatul true reuseste intotdeauna, iar fail esueaza intotdeauna.
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. Definitia lui ar putea fi:
repeat.
repeat :- repeat.
Acest predicat este predefinit in mediul SWI-Prolog.
Un predicat echivalent ciclului for, care nu este predefinit in SWI-Prolog, se poate implementa astfel:
% for(Variabla, ValoareInitiala, ValoareFinala, Pas)
for(I, I, I, 0).
for( _, _ , _, 0) :- !, fail.
for(I, I, F, S) :-
S > 0, (I > F, !, fail ; true)
; S < 0, (I < F, !, fail ; true).
for(V, I, F, S) :- I1 is I + S, for(V, I1, F, S).
Exemple de apeluri:
a :- for(X, 10, -10, -3.5), write(X), tab(2), fail ; true.
b :- for(X, -10, 10, 3.5), write(X), tab(2), fail ; true.
c :- for(X, 10, 10, 0), write(X), tab(2), fail ; true.
a.
10 6.5 3.0 -0.5 -0 -7.5
Yes
b.
-10 -6.5 -3.0 0.5 0 7.5
Yes
c.
Yes
Predicatul for esueaza pentru apeluri incorecte (cicluri infinite) de genul:
for(X, -10, 10, 0).
for(X, 10, -10, 2).
for(X, -10, 10, -2).
Predicate de tratare a clauzelor drept termeni
Predicatele din aceasta categorie permit: construirea unei structuri care reprezinta o clauza in baza de cunostinte, identificarea clauzelor din program, adaugarea sau eliminarea unor clauze, reprezentate printr‑o structura, in baza de cunostinte a sistemului.
clause(Antet, Corp)
Satisfacerea scopului clause(Antet, Corp) necesita unificarea argumentelor Antet si Corp cu antetul si corpul unei clauze existente in baza de cunostinte Prolog. La apelul acestui predicat, Antet trebuie sa fie instantiat astfel incat predicatul principal al clauzei sa fie cunoscut. Daca nu exista clauze pentru acest predicat, scopul esueaza. Daca exista mai multe clauze care definesc predicatul Antet, scopul clause(Antet, Corp) va avea mai multe solutii, fiecare solutie corespunzand unei clauze de definire a predicatului Antet. Daca o clauza este fapt, Corp se instantiaza la constanta true.
Exemplu Considerand definitia predicatului de concatenare a doua liste ca fiind deja introdusa in baza de cunostinte Prolog:
% conc(Lista1, Lista2, ListaRez)
conc([], L, L).
conc([Prim|Rest1], L2, [Prim|Rest3]) :- conc(Rest1, L2, Rest3).
se obtin urmatoarele rezultate:
?- clause(conc(A, B, C), Corp).
A = [ ],
B = C,
Corp = true
A = [_G463|_G464],
C = [_G463|_G467],
Corp = conc (_G464, B, _G467)
No
Utilizand predicatul clause se poate defini un predicat listc de afisare a clauzelor care definesc un predicat:
% listc(Antet) - afiseaza toata clauzele din baza de cunostinte
% care definesc scopul Antet
listc(Antet) :-
clause(Antet, Corp),
afis(Antet, Corp),
write('.'), nl, fail.
listc( _ ).
afis(Antet, true) :- !, write(Antet). %1
afis(Antet, Corp) :- write(Antet), write(':'), write('-'), write(Corp).
Efectul acestui program, considerand definitia anterioara a scopului conc, este urmatorul:
?- listc(conc(_, _, _)).
conc([ ], _G304, _G304).
conc([_G375|_G376],_G304,[_G375|_G379]:-
conc(_G376,_G304,_G379).
Yes
In definirea primei clauze a predicatului afis utilizarea predicatului cut este esentiala deoarece afisarea clauzelor este bazata pe procesul de backtracking generat intentionat de introducerea predicatului fail in prima clauza a predicatului listc. Regula %1 din predicatul afis este necesara, deoarece faptele sunt reguli avand corpul format din scopul true. Aceasta reprezentare ne arata ca faptele si regulile au aceeasi reprezentare in Prolog (toate reprezinta clauze).
Exemplu. Pentru urmatorul predicat:
p(1).
p(X) :- X > 2, X < 5.
p(X).
predicatul clause da urmatoarele raspunsuri:
?- clause(p(X), Corp).
X
Corp = true ;
Corp = (X > 2, X < 5) ;
Corp = true ;
No
asserta(Clauza), assertz(Clauza), assert(Clauza),
Predicatul asserta(Clauza) reuseste intotdeauna o singura data si are ca efect lateral adaugarea clauzei Clauza la inceputul bazei de cunostinte Prolog. Predicatele assertz(Clauza) si assert(Clauza) au o comportare similara, cu exceptia faptului ca argumentul Clauza este adaugat la sfarsitul bazei de cunostinte. Argumentul Clauza trebuie sa fie instantiat la o valoare ce reprezinta o clauza Prolog. Actiunea de adaugare a unei clauze prin asserta, assert sau assertz nu este eliminata de procesul de backtracking. Clauza adaugata va fi eliminata numai in urma unei cereri explicite prin intermediul predicatului retract.
retract(Clauza)
Predicatul retract(Clauza) are ca efect eliminarea din baza de cunostinte a unei clauze, care unifica cu argumentul Clauza. Predicatul reuseste daca o astfel de clauza exista; la fiecare resatisfacere se elimina din baza de cunostinte o noua clauza, care unifica cu argumentul. Clauza trebuie sa fie argument instantiat la apel. Odata ce o clauza este eliminata din baza de cunostinte prin retract, clauza nu va mai fi readaugata, daca se face backtracking pe retract.
Exemplu.
adaug :- asserta(prezent(nero)), asserta(prezent(cezar)).
scot :-retract(prezent(nero)).
actiune1 :- adaug, listc(prezent(X)).
actiune2 :- scot, listc(prezent(X)).
?- actiune1.
prezent(cezar).
prezent(nero).
Yes
?- actiune2.
prezent(cezar).
Yes
Utilizand predicatul retract, se poate defini un predicat care are rolul de a elimina din baza de cunostinte toate clauzele care definesc un predicat. Acest predicat, numit retractall(Antet), este predefinit in anumite implementari Prolog. Definitia lui pe baza predicatului retract este:
% retractall(Antet) - elimina toate clauzele care definesc scopul Antet
retractall(Antet) :- retract(Antet), fail.
retractall(Antet) :- retract( (Antet :- Corp) ), fail.
retractall( _ ).
Observatie: In SWI-Prolog, atunci cand se adauga/elimina o regula in/din baza de date in mod dinamic cu assert/retract, regula trebuie pusa intre paranteze rotunde sau data ca o structura:
assert( (p(X) :- X=1 ; X=2) ).
retract( ':-'(p(X), ';'(X=1, X=2))).
In definirea scopului p s‑a utilizat simbolul ;. Acesta este un operator predefinit in Prolog, care marcheaza o disjunctie de scopuri, deci are semnificatia de disjunctie logica. Efectul operatorului ; poate fi reprezentat in urmatorul fel:
X ; Y :- X.
X ; Y :- Y.
Deci asa cum operatorul Prolog , corespunde unei conjunctii de scopuri, operatorul ; corespunde unei disjunctii de scopuri.
Predicatele asserta, assertz si retract pot fi utile in diverse situatii datorita efectelor lor laterale. Ele pot simula, de exemplu, un fel de mecanism de variabile globale in Prolog. Se considera exemplul de implementare a unui generator de numere aleatoare in Prolog. Se defineste predicatul aleator(R, N), care genereaza un numar aleator N in intervalul [1..R], pornind de la o samanta fixata si folosind metoda congruentei liniare. De fiecare data cand trebuie generat un numar aleator, valoarea acestuia este determinata folosind samanta existenta si o noua samanta este calculata si memorata ca fapt Prolog pana la noul apel. Vechea samanta este eliminata din baza de cunostinte .
% aleator(R, N) - instantiaza N la un numar aleator intre 1 si R
init :- assert(samanta(11)).
aleator(R, N) :-
samanta(S),
modul(S, R, N1),
N is N1 + 1,
retract(samanta(S)),
N2 is (125 * S +1),
modul(N2, 4096, SamantaNoua),
asserta(samanta(SamantaNoua)).
modul(X, Y, X) :- X < Y.
modul(X,Y,Z) :- % Predicatul modulo este predefinit
X >= Y, % in anumite implementari
X1 is X - Y,
modul(X1, Y, Z).
Inainte de apelul predicatului aleator, se apeleaza, o singura data, predicatul init, pentru a initializa samanta. La apelul predicatului aleator se obtin urmatoarele rezultate:
?- aleator(100, N).
N = 12
?- aleator(100, N).
N = 77
?- aleator(100, N).
N = 66
Daca se doreste afisarea continua a numerelor aleatoare, o prima varianta este aceea de a crea un ciclu infinit de afisare a acestor numere, efect realizat de predicatul nr_aleat(R) care afiseaza numere aleatoare in intervalul [1..R].
% nr_aleat(R) - afiseaza la infinit numere aleatoare intre 1 si R
nr_aleat(R) :- repeat, aleator(R, X), write(X), nl, fail.
Predicatul fail introdus in definitia predicatului nr_aleat(R) determina intrarea in procesul de backtracking si datorita predicatului repeat, cele doua scopuri aleator(R, X) si write(X) se vor satisface la infinit. Trebuie observat faptul ca numai scopul repeat se resatisface; scopurile aleator si write sunt la prima satisfacere de fiecare data.
In cazul in care se doreste construirea unui ciclu care se va termina daca o anumita conditie este indeplinita, se poate reformula predicatul de generare a numerelor aleatoare astfel:
nr_aleat1(R) :-
repeat, aleator(R, X), write(X), nl,
write('Continuati? [d/n]'), read('n').
Apeland predicatul, se va obtine urmatoarea secventa de numere aleatoare:
?- nr_aleat1(10).
2
Continuati? [d/n] d.
7
Continuati? [d/n] d.
6
Continuati? [d/n] n.
Yes
functor(Term, Funct, N)
Predicatul reuseste daca cele trei argumente unifica astfel: Term este o structura cu functor Funct si aritate N. Predicatul poate fi folosit in doua moduri:
Term este argument instantiat si predicatul reuseste daca Term este atom sau structura si esueaza in caz contrar. In caz de succes, Funct si N sunt instantiate la functorul, respectiv aritatea lui Term. Un atom este considerat ca fiind o structura de aritate 0.
Term este argument neinstantiat, iar Funct si N sunt instantiate, specificand functorul si numarul de argumente. In acest caz, scopul reuseste intotdeauna si Term va fi instantiat la o structura cu functorul si numarul de argumente specificate. Argumentele structurii construite in acest mod sunt neinstantiate.
Exemple
?- functor(auto(honda, culoare(rosie)), Funct, N).
Funct = auto,
N = 2
?- functor(a + b, F, N).
F = +,
N = 2
?- functor(albastru, F, N).
F = albastru,
N = 0
?- functor ([a, b, c], !, 3).
No
?- functor(Term, pasi_plan, 3).
Term = pasi_plan(_G405, _G406, _G407)
arg(N, Term, Arg)
Predicatul arg(N, Term, Arg) are ca efect obtinerea celui de al N-lea argument al unei structuri Term, acest argument devenind instantierea lui Arg. N si Term trebuie sa fie argumente instantiate. Arg poate fi instantiat, caz in care predicatul reuseste daca Arg este argumentul N din structura Term, sau Arg poate fi neinstantiat, caz in care se va instantia la cel de al N‑lea argument al lui Term.
?- arg(2, poseda(mihai, frumoasa(portocala)), Arg).
Arg = frumoasa(portocala)
?- arg (2,[a, b, c], X).
X = [b, c]
?- arg(1, a + (b + c), b).
No
Scop = .. [Functor | ListArg]
Predicatul =.., numit univ, este un predicat folosit ca operator infixat. El poate fi folosit in trei moduri:
Scop este instantiat. In acest caz predicatul reuseste, daca Scop este o structura, iar Functor si ListArg se instantiaza la functorul, respectiv lista argumentelor acelei structuri.
Scop este neinstantiat, iar Functor si ListArg sunt instantiate la un functor, respectiv la o lista de argumente a unei structuri. Daca valorile lui Functor si ListArg sunt corecte, predicatul reuseste si Scop va fi instantiat la structura creata cu functorul si lista de argumente date.
Toti trei parametrii sunt instantiati si predicatul reuseste, daca Functor si ListArg sunt functorul, respectiv lista argumentelor structurii Scop.
Acest predicat are o importanta deosebita in Prolog, deoarece el permite sinteza dinamica a scopurilor Prolog, deci construirea de clauze pe parcursul executiei programului. Lucrul acesta este posibil deoarece clauzele Prolog au tot forma unor structuri alcatuite din functor si argument, sintaxa codului fiind aceeasi cu sintaxa datelor in Prolog.
Exemple
?- sir(a, b, c) =.. X.
X = [sir, a, b, c]
?- (f(a) + g(b)) =.. [+, f(X), Y].
X = a,
Y = g(b)
a + b + c =.. L. % a + b + c = '+'(a, '+'(b, c))
L=[+, a+b, c]
a b + c =.. L. % a b + c = '+'(' '(a, b), c)
L = [+, a b, c]
a + b c =.. L. % a + b c = '+'(a, ' '(b, c))
L = [+, a, b c]
?- Scop =.. [parinte, mihai, gina].
Scop = parinte(mihai, gina)
?- Scop =.. [membru, a, [a, b, c]].
Scop = membru(a, [a, b, c])
?- conc([1, 2], [3], [1, 2, 3]) =.. [Functor | ListArg]
Functor = conc,
ListArg = [[1, 2], [3], [1, 2, 3]]
?- S =.. [f, X, a,Y].
S = f(X, a, Y)
?- f =.. L.
L = [f]
Folosind univ putem verifica faptul ca listele se reprezinta prin structuri cu punct de forma: '.'(PrimulElement, RestulListei). Lista [1, 2] este totuna cu '.'(1, [2]) sau cu '.'(1, '.'(2, [])).
Exemplu.
[1,2] =.. L.
L = ['.', 1, [2]]
listing, listing(+Nume), listing(+Nume/Aritate),
listing(+[Nume/Aritate.])
Predicatul listing tipareste din baza de cunostinte toate clauzele, toate clauzele unui predicat sau toate clauzele unor predicate dintr‑o lista. De exemplu, pentru predicatele:
p.
p(1).
p(X) :- X > 2, X < 5.
p(X).
p(1,2).
p(X,Y) :- X < Y.
se pot face urmatoarele interogari (redenumirile variabilelor sunt facute automat de sistem):
listing(p/1).
p(
p(A) :-
A >
A <
p( _ ).
Yes
listing([p/0, p/2]).
p.
p(
p(A, B) :-
A < B.
Yes
Predicate pentru executia dinamica a scopurilor
call(Scop)
Se presupune ca argumentul Scop este instantiat la un scop Prolog. Predicatul call(Scop) reuseste daca Scop reuseste si esueaza in caz contrar. La prima vedere acest predicat pare redundant, deoarece executia scopului Scop se poate face direct in Prolog. Daca se doreste executia dinamica a scopurilor care au fost sintetizate pe parcursul executiei unui program Prolog, atunci predicatul call este necesar. In plus, daca nu se cunoaste scopul de executat, de exemplu se doreste executia a diverse scopuri cu argumente diferite sau cu aceleasi argumente, se poate folosi predicatul call cu argument o variabila, care se va instantia in functie de intrebarea utilizatorului sau de context.
Executia dinamica a scopurilor poate fi exprimata prin urmatoarea secventa de scopuri:
obtine(Functor), calculeaza(ArgList), Scop =.. [Functor | ArgList], call(Scop),
Se prezinta in continuare diverse variante de predicate Prolog care aplica un predicat primit ca argument fiecarui element al unei liste sau al mai multor liste, rezultatele fiecarei aplicari fiind cumulate intr‑o lista rezultat. Acest tip de predicate reprezinta o categorie de prelucrari ale listelor, frecvent intalnite si necesare.
Se defineste intai predicatul mapcar(Pred, ListaArg, ListaRez), care primeste doua argumente instantiate Pred si ListaArg si calculeaza ListaRez. Pred este un predicat Prolog care are la randul lui doua argumente: primul, instantiat, este folosit in prelucrare pentru obtinerea celui de al doilea si este obtinut ca element curent din ListaArg; cel de al doilea, neinstantiat, este calculat de Pred si este depus ca element curent in ListaRez. In programul care urmeaza se vor folosi ca predicate Pred urmatoarele:
prim(Lista, El) care obtine primul element dintr‑o lista,
neg(Element, -Element) care schimba semnul unui element si
adaug(Nume, NumeFrumos) care adauga in fata unui nume apelativul de politete dna daca persoana este femeie si dl daca persoana este barbat.
Se observa ca argumentele lui Pred din definitia lui mapcar pot fi atat elemente atomice (atomi sau numere), cat si liste.
% Se definesc predicatele de prelucrare a listelor: mapcar, mapcar2 si mapcarnl.
prim([], []).
prim([X | _ ], X).
neg(X, Y) :- Y is -1 * X.
adaug(Nume, NumeFrumos) :-
write(Nume),
write(' este femeie sau barbat (f/b)? '),
read(Sex),
(Sex = b, NumeFrumos = [dl, Nume] ;
Sex = f, NumeFrumos = [dna, Nume]).
% mapcar(Pred, ListaArg, ListaRez) - evalueaza predicatul
% Pred pentru fiecare element din lista ListaArg si
% construieste rezultatul in ListaRez.
% Predicatul Pred are doua argumente, atomice sau liste.
mapcar( _ , [], []).
mapcar(P, [Arg | RestArg], [X | Rest]) :-
Scop =.. [P, Arg, X],
call(Scop),
mapcar(P, RestArg, Rest).
Comportarea acestui program este urmatoarea:
?- mapcar(prim, [[alain, turing], [ada, byron]], Rez).
Rez = [alain, ada]
?- mapcar(neg, [1, 2, 3, 4], Rez).
Rez = [-1, -2, -3, -4]
?- mapcar(adaug, [maria, mihai, george, ana], Rez).
maria este femeie sau barbat (f/b)? f.
mihai este femeie sau barbat (f/b)? b.
george este femeie sau barbat (f/b)? b.
ana este femeie sau barbat (f/b)? f.
Rez = [[dna, maria], [dl, mihai], [dl, george], [dna, ana]]
Daca se doreste executia unor prelucrari similare, dar pentru predicate Prolog care admit trei argumente, primele doua instantiate si cel de al treilea sintetizat de predicatul Pred pe baza primelor doua, se poate defini de o maniera similara predicatul:
mapcar2(Pred, ListaArgPrim, ListaArgSecund, ListaRez)
Se va demonstra functionarea acestui predicat in cazurile in care predicatul Pred, care se mapeaza pe elementele listelor ListaArgPrim si ListaArgSecund, este intai adun(A, B, Rez), care calculeaza in Rez suma lui A si B, apoi conc(List1, Lista2, ListaRez), care concateneaza doua liste si depune rezultatul in ListaRez. Argumentele predicatului Pred pot fi argumente atomice sau liste.
conc([], L, L).
conc([X|Rest], L, [X|Rest1]) :- conc(Rest, L, Rest1).
adun(A, B, Rez) :- Rez is A + B.
% mapcar2(Pred, ListaArgPrim, ListaArgSecund, ListaRez) -
% evalueaza predicatul Pred pentru fiecare pereche
% de argumente, unul din ListaArgPrim, celalalt din ListaArgSecund
% si construieste rezultatul in ListaRez.
% Predicatul Pred are trei argumente, atomice sau liste.
mapcar2( _ , [], _ , []).
mapcar2(P, [Arg1 | RestArg1], [Arg2 | RestArg2], [X | Rest]) :-
Scop =.. [P, Arg1, Arg2, X],
call(Scop),
mapcar2(P, RestArg1, RestArg2, Rest).
?- mapcar2(adun, [1, 2, 3, 4], [10, 20, 30, 40], Rez).
Rez = [11, 22, 33, 44]
?- mapcar2(conc, [[1, 2], [a, b]], [[3, 4], [c, d]], Rez).
Rez = [[1, 2, 3, 4], [a, b, c, d]]
Se observa ca este necesara definirea unui predicat de tip mapcar diferit pentru fiecare categorie de aritate N a predicatelor care pot instantia legal Pred. Daca se impune restrictia conform careia predicatul Pred poate avea numai argumente atomice, atunci se poate defini predicatul mapcarnl(Pred, ListaListeArg, ListaRez) cu un efect similar. ListaListeArg este fie o lista de elemente atomice, daca Pred are doua argumente, dintre care primul instantiat va fi obtinut ca elementul curent al acestei liste, fie o lista de liste, unde fiecare element lista din ListaListeArg contine primele N‑1 argumente atomice ale lui Pred.
% mapcarnl(Pred, ListaListeArg, ListaRez) -
% evalueaza predicatul Pred cu primele N-1 argumente din lista
% ListaListeArg si construieste rezultatul in ListaRez.
% Predicatul Pred poate avea oricate argumente, dar toate trebuie sa fie
% atomice.
mapcarnl(_, [], []).
mapcarnl(P, [Arg|RestArg], [X|Rest]) :-
(atomic(Arg), FormaScop = [P,Arg,X] ;
not(atomic(Arg)), conc([P], Arg, Temp),
conc(Temp, [X], FormaScop)), Scop =.. FormaScop,
call(Scop), mapcarnl(P, RestArg, Rest).
?- mapcarnl(neg, [1, 2, 3, 4], Rez]
Rez = [-1, -2, -3, -4]
?- mapcarnl(adun, [[1, 10], [2, 20], [3, 30], [4, 40]], Rez).
Rez = [11, 22, 33, 44]
findall(X, Scop, Lista)
Predicatul findall(X, Scop, Lista) are ca efect obtinerea tuturor termenilor X care satisfac predicatul Scop in baza de cunostinte a programului si cumularea acestor termeni in lista Lista. Scop trebuie sa fie instantiat la un predicat Prolog in care, in mod normal, trebuie sa apara X. Daca Scop nu reuseste, findall reuseste, instantiind Lista la lista vida.
Exemple
prieten(vlad, ana).
prieten(vlad, george).
prieten(vlad, mihai).
prieten(liviu, ana).
?- findall(X, prieten(vlad, X), L).
L = [ana, george, mihai]
Efectul predicatului findall poate fi explicat si prin definitia explicita a unui predicat cu acelasi efect: find_all(X, Scop, Lista)
% find_all(Arg, Scop, Lista) -
% are acelasi efect ca predicatul standard findall(Arg, Scop, Lista).
prieten(vlad, ana).
prieten(vlad, george).
prieten(vlad, mihai).
prieten(liviu, ana).
find_all(X, S, _) :- asserta(stiva(baza_stivei)),
call(S), % Scopul S contine X.
asserta(stiva(X)), fail.
find_all( _ , _ , L) :- colecteaza([], M), !, L = M.
colecteaza(S, L) :- urmator(X), !, colecteaza([X | S], L).
colecteaza(L, L).
urmator(X) :- retract(stiva(X)), !, X = baza_stivei.
?- find_all(X, prieten(vlad, X), L).
L = [ana, george, mihai]
?- find_all(X, prieten(toma, X), L).
L = []
In definitia predicatului find_all se observa folosirea unei tehnici de programare Prolog interesante. Utilizand predicatul asserta se simuleaza o structura de stiva in Prolog. Baza stivei este marcata de faptul stiva(baza_stivei) si in stiva se introduc, ca fapte suplimentare adaugate la inceputul bazei de cunostinte Prolog, valorile X care satisfac scopul S sub forma stiva(X), cu X instantiat de apelul call(S). Acest lucru este executat pentru toate solutiile posibile ale predicatului call(S), datorita predicatului fail introdus in finalul definitiei primei clauze a predicatului find_all. In acest fel, toate valorile lui X din baza de cunostinte Prolog, care satisfac S, sunt introduse in stiva simulata. Cea de a doua clauza a predicatului find_all, care se executa dupa ce nu mai exista nici o posibilitate de resatisfacere a primei clauze, are ca scop golirea stivei si colectarea valorilor introduse in stiva, pana la baza ei, stiva(baza_stivei), in lista M.
bagof(X, Scop, Lista)
Predicatul bagof(X, Scop, Lista) are ca efect colectarea in Lista a tuturor termenilor X care satisfac Scop, tinand cont de diversele instantieri posibil diferite ale celorlalte variabile din Scop. Scop este un argument, care trebuie sa fie instantiat la un scop Prolog. Daca Scop nu contine alte variabile in afara lui X, atunci efectul predicatului bagof este identic cu cel al lui findall. Daca Scop nu reuseste cel putin o data atunci bagof esueaza.
Exemple
clasificare(a, vocala).
clasificare(b, consoana).
clasificare(c, consoana).
clasificare(d, consoana).
clasificare(e, vocala).
clasificare(f, consoana).
?- findall(X, clasificare(X, C), L).
L = [a, b, c, d, e, f] % o solutie
?- bagof(X, clasificare(X, C), L).
C = vocala,
L = [a, e];
C = consoana,
L = [b, c, d, f];
No % doua solutii
setof(X, Scop, Lista)
Predicatul setof(X, Scop, Lista) are acelasi efect cu predicatul bagof, cu exceptia faptului ca valorile cumulate in lista Lista sunt sortate crescator si se elimina aparitiile multiple de valori, deci Lista devine o multime ordonata. Daca Scop nu reuseste cel putin o data, atunci setof esueaza.
Considerand faptele de definire a consoanelor si variabilelor prezentate anterior, se poate construi urmatorul exemplu, in care mai introducem doua fapte:
clasificare(e, vocala).
clasificare(d, consoana).
?- bagof(X, clasificare(X, C), L).
C = vocala,
L = [e, a, e] % vocala e apare de doua ori
C = consoana,
L = [d, b, c, d, f] % consoana d apare de doua ori
No
?- setof(X, clasificare(X, C), L).
C = vocala,
L = [a, e] % vocalele apar o singura data
C = consoana,
L = [b, c, d, f] % consoanele apar o singura data
No
Operatori definiti de utilizator
Limbajul Prolog permite definirea de noi operatori de catre programator, prin introducerea in program a unor clauze de forma speciala, numite directive. Directivele actioneaza ca o definire de noi operatori ai limbajului, in care se specifica numele, precedenta si tipul (infixat, prefixat sau postfixat) operatorului. Se reaminteste faptul ca orice operator Prolog este de fapt o structura, care are ca functor numele operatorului si ca argumente, argumentele operatorului, dar este scris intr‑o sintaxa convenabila. La definirea de noi operatori in Prolog, se creeaza de fapt noi structuri, carora le este permisa o sintaxa speciala, conform definitiei corespunzatoare a operatorului. Din acest motiv, nu se asociaza nici o operatie operatorilor definiti de programator. Operatorii noi definiti sunt utilizati ca functori, numai pentru a combina obiecte in structuri si nu pentru a executa actiuni asupra datelor, desi denumirea de operator ar putea sugera o astfel de actiune.
De exemplu, in loc de a utiliza structura:
are(coco, pene)
se poate defini un nou operator are
:- op(600, xfx, are).
caz in care este legala expresia
coco are pene.
Definirea de noi operatori se face cu ajutorul directivei:
:- op(precedenta-operator, tip-operator, nume-operator).
Operatorii sunt atomi, iar precedenta lor trebuie sa fie o valoare intreaga intr‑un anumit interval si corelata cu precedenta operatorilor predefiniti in limbaj. Tipul operatorilor fixeaza caracterul infixat, prefixat sau postfixat al operatorului si precedenta operanzilor sai. Tipul operatorului se defineste utilizand una din urmatoarele forme standard:
(1) operatori infixati: xfx xfy yfx
(2) operatori prefixati: fx fy
(3) operatori postfixati: xf yf
unde f reprezinta operatorul, iar x si y operanzii sai. Utilizarea simbolului x sau a simbolului y depinde de precedenta operandului fata de operator. Precedenta operanzilor se defineste astfel:
un argument intre paranteze sau un argument nestructurat are precedenta 0;
un argument de tip structura are precedenta egala cu cea a functorului operator.
Observatie: In SWI-Prolog, cu cat valoare‑precedenta-operator este mai mare, cu atat operatorul are o precedenta mai mica si invers.
Semnificatiile lui x si y in stabilirea tipului operatorului sunt urmatoarele:
x reprezinta un argument (operand) cu precedenta strict mai mica decat cea a functorului (operatorului) f
precedenta( x ) < precedenta( f )
y reprezinta un argument (operand) cu precedenta mai mica sau egala cu cea a functorului (operandului) f
precedenta( y ) precedenta( f )
Aceste reguli ajuta la eliminarea ambiguitatii operatorilor multipli cu aceeasi precedenta. De exemplu, operatorul predefinit in Prolog - (minus) este definit din punct de vedere al tipului ca yfx, ceea ce inseamna ca structura este interpretata ca si nu ca . Daca acest operator ar fi fost definit ca xfy, atunci interpretarea structurii ar fi fost .
Numele operatorului poate fi orice atom Prolog, care nu este deja definit in Prolog. Se poate folosi si o lista de atomi, daca se definesc mai multi operatori cu aceeasi precedenta si acelasi tip.
Exemple
:- op(100, xfx, [este, are]).
:- op(100, xf, zboara).
coco are pene.
coco zboara.
coco este papagal.
bozo este pinguin.
?- Cine are pene.
Cine = coco
?- Cine zboara.
Cine = coco
?- Cine este Ce.
Cine = coco,
Ce = papagal
Cine = bozo,
Ce = pinguin
No
In conditiile in care se adauga la baza de cunostinte anterioara si definitia operatorilor daca, atunci si si
:- op(870, fx, daca).
:- op(880, xfx, atunci).
:- op(880, xfy, si).
urmatoarea structura este valida in Prolog:
daca Animalul are pene
si Animalul zboara
atunci Animalul este pasare.
Notatia cu operatori permite programatorului sa ajusteze sintaxa programelor conform propriilor dorinte. Intelegerea programelor este mult imbunatatita atunci cand se folosesc operatori.
Exemplu: Daca definim operatorii joaca si si:
op(300,xfx, joaca).
op(200, xfy, si).
atunci urmatorii termeni sunt obiecte legale din punct de vedere sintactic:
Termen1 = tom joaca fotbal si tenis.
Termen2 = susan joaca tenis si volei si ping‑pong.
Calculele aritmetice sunt facute prin proceduri predefinite. Evaluarea unei
expresii aritmetice este fortata de procedura is si de predicatele de comparatie <, =<, etc. De exemplu:
X = 3 / 2
X = 3 / 2
X is 3 / 2
X = 1.5
Exercitii propuse
EP1. Fie urmatorul program Prolog:
este(a).
este(b).
este(c).
exista(X) :- este(X) ; true.
Cate solutii are fiecare dintre urmatoarele scopuri:
exista(A), exista(B), exista(C), A = a, B = b, C = c.
exista(A), exista(B), exista(C), !, A = a, B = b, C = c.
exista(A), !, exista(B), exista(C).
EP2. Sa se scrie un predicat, care citeste in bucla numere si raspunde daca sunt prime sau nu, pana la intalnirea unui numar negativ, folosind predicatul predefinit repeat.
EP3. Sa se scrie un program care calculeaza numarul de zile dintre doua date exprimate sub forma Zi-Luna, presupunand ca datele se refera la acelasi an. Caracterul "-" se va defini ca un operator infixat.
Ex: interval(3-martie, 7-aprilie, I) produce I = 35.
EP Sa se scrie un program care citeste propozitii simple (afirmatii si intrebari, pe care le adauga in baza de cunostinte Prolog), avand una din formele:
_ este un _.
_ este o _.
Un _ este un/o _.
O _ este un/o _.
Este _ un/o _ ?
si raspunde adecvat (da, nu sau necunoscut) la intrebari pe baza afirmatiilor introduse. Exemplu: Radu este un student. Un om este o fiinta. Radu este un om. Este Maria o persoana?
EP5. Sa se defineasca operatorii Prolog este, are, un etc. astfel incat sa poata fi introduse clauze de forma:
diana este secretara lui toma.
maria are un pian.
si sistemul sa poata raspunde la intrebari de tipul:
?-Cine este secretara lui toma.
Cine=Diana
?-diana este Ce.
Ce=secretara lui toma
?-maria are un Ce.
Ce=pian
EP6. Sa se scrie un program Prolog de evaluare a unei expresii aritmetice care contine variabile, constante intregi, paranteze si operatorii: +, -, *, /. Valorile variabilelor sunt date sub forma de fapte Prolog, prin predicatul valoare(Variabila, Valoare); de exemplu valoare(x, 100) sau valoare(y, -50).
EP7. Sa se scrie un program care determina negarea unei formule in logica cu propozitii. Conectorii logici considerati sunt: not, and, or si implies. Sa se dea definitii adecvate pentru acesti operatori.
Ex: neaga(p implies (q and not r),E) va produce E = p and (not q or r)
EP8. Utilizand predicatul bagof, scrieti predicatul parti_mult(Mult, Submult) care calculeaza in Submult multimea submultimilor multimii Mult. Reprezentati multimile sub forma de liste.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 1842
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved