This shows you the differences between two versions of the page.
pp:21:laboratoare:prolog:legare-executie [2021/05/09 19:34] bot.pp created |
pp:21:laboratoare:prolog:legare-executie [2021/05/10 03:09] (current) bot.pp |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Prolog: Legare și execuție ====== | ====== Prolog: Legare și execuție ====== | ||
- | * Data publicării: 09.05.2021 * Data ultimei modificări: 09.05.2021 | + | * Data publicării: 09.05.2021 |
+ | * Data ultimei modificări: 09.05.2021 | ||
===== Obiective ===== | ===== Obiective ===== | ||
Line 7: | Line 8: | ||
Scopul acestui laborator este introducerea unor noțiuni mai avansate de Prolog: | Scopul acestui laborator este introducerea unor noțiuni mai avansate de Prolog: | ||
- | * procesul de backtracking realizat de Prolog * lucrul cu variabile neinstanțiate (nelegate)''%% * controlul execuției și predicatul cut.%%'' | + | * procesul de backtracking realizat de Prolog |
+ | * lucrul cu variabile neinstanțiate (nelegate) | ||
+ | * controlul execuției și predicatul cut. | ||
===== Puterea generativă a limbajului ===== | ===== Puterea generativă a limbajului ===== | ||
Line 16: | Line 19: | ||
<code> | <code> | ||
- | % lungime(+Lista,-Lungime) | + | % lungime(+Lista,-Lungime) |
lungime([],0). | lungime([],0). | ||
lungime([_ | R], N) :- lungime(R, N1), N is N1 + 1. | lungime([_ | R], N) :- lungime(R, N1), N is N1 + 1. | ||
</code> | </code> | ||
- | ''%%?- lungime([1,2,3],N).%%'' | + | <code> |
+ | ?- lungime([1,2,3],N). | ||
+ | N = 3. | ||
- | ''%%N = 3.%%'' | + | </code> |
- | + | În exemplul de mai sus se va încerca satisfacerea scopului ''%%lungime([1,2,3],N).%%'' prin instanțierea convenabilă a variabilei ''%%N%%''. În acest caz soluția este unică, dar așa cum am văzut anterior, putem avea situații în care există mai multe unificări posibile. Putem folosi faptul că se încearcă resatisfacerea unui scop în mod exhaustiv pentru a genera multiple rezultate. | |
- | În exemplul de mai sus se va încerca satisfacerea scopului ''%%lungime(\[1,2,3\],N).%%'' prin instanțierea convenabilă a variabilei ''%%N%%''. În acest caz soluția este unică, dar așa cum am văzut anterior, putem avea situații în care există mai multe unificări posibile. Putem folosi faptul că se încearcă resatisfacerea unui scop în mod exhaustiv pentru a genera multiple rezultate. | + | |
În exemplul de mai jos vom defini predicatul ''%%membru(?Elem,+List)%%'' care verifică apartenența unui element la o listă: | În exemplul de mai jos vom defini predicatul ''%%membru(?Elem,+List)%%'' care verifică apartenența unui element la o listă: | ||
Line 35: | Line 39: | ||
</code> | </code> | ||
- | Putem folosi acest predicat pentru a obține un răspuns: ''%%?- membru(3, [1, 2, 3, 4, 5]).%%'' ''%%true .%%'' | + | Putem folosi acest predicat pentru a obține un răspuns: |
+ | <code> | ||
+ | ?- membru(3, [1, 2, 3, 4, 5]). | ||
+ | true. | ||
+ | |||
+ | </code> | ||
Sau putem să îl folosim pentru a genera pe rând toate elementele unei liste: | Sau putem să îl folosim pentru a genera pe rând toate elementele unei liste: | ||
Line 47: | Line 56: | ||
</code> | </code> | ||
- | Inițial, scopul ''%%membru(N, \[1,2,3\]).%%'' va unifica cu faptul ''%%membru(Elem, \[Elem | \_\]).%%'', în care ''%%Elem = N%%'' și ''%%Elem = 1%%'', din care rezultă instanțierea ''%%N = 1%%''. Apoi se va încerca unificarea cu antetul de regulă ''%%membru(Elem, \[\_ | Rest\])%%'', în care ''%%Elem = N%%'', iar ''%%Rest = \[2, 3\]%%''. Acest lucru implică satisfacerea unui nou scop, ''%%membru(N, \[2, 3\]).%%''. Noul scop va unifica, de asemenea, cu faptul de la linia 1, ''%%membru(Elem, \[Elem | \_\]).%%'', din care va rezulta ''%%N = 2%%''. Asemănător se va găsi și soluția ''%%N = 3%%'', după care nu va mai reuși nicio altă unificare. | + | Inițial, scopul ''%%membru(N, [1,2,3]).%%'' va unifica cu faptul ''%%membru(Elem, [Elem | _]).%%'', în care ''%%Elem = N%%'' și ''%%Elem = 1%%'', din care rezultă instanțierea ''%%N = 1%%''. Apoi se va încerca unificarea cu antetul de regulă ''%%membru(Elem, [_ | Rest])%%'', în care ''%%Elem = N%%'', iar ''%%Rest = [2, 3]%%''. Acest lucru implică satisfacerea unui nou scop, ''%%membru(N, [2, 3]).%%''. Noul scop va unifica, de asemenea, cu faptul de la linia 1, ''%%membru(Elem, [Elem | _]).%%'', din care va rezulta ''%%N = 2%%''. Asemănător se va găsi și soluția ''%%N = 3%%'', după care nu va mai reuși nicio altă unificare. |
Pentru a exemplifica utilizarea acestui mecanism, vom considera următorul exemplu în care dorim generarea, pe rând, a tuturor permutărilor unei liste: | Pentru a exemplifica utilizarea acestui mecanism, vom considera următorul exemplu în care dorim generarea, pe rând, a tuturor permutărilor unei liste: | ||
- | <html><code prolog></html> % remove(+Elem, +Lista, -ListaNoua) remove(E, [E | R], R). remove(E, [F | R], [F | L]) :- remove(E, R, L). | + | <code> |
+ | % remove(+Elem, +Lista, -ListaNoua) | ||
+ | remove(E, [E | R], R). | ||
+ | remove(E, [F | R], [F | L]) :- remove(E, R, L). | ||
- | % perm(+Lista, -Permutare) perm([], []). perm([F | R], P) :- perm(R, P1), remove(F, P, P1). <html></code></html> | + | </code> |
+ | <code> | ||
+ | % perm(+Lista, -Permutare) | ||
+ | perm([], []). | ||
+ | perm([F | R], P) :- perm(R, P1), remove(F, P, P1). | ||
- | Observați ca am definit predicatul ''%%remove(+Elem, +Lista, -ListaNoua)%%'', care șterge un element dintr-o listă. Rolul acestuia în cadrul regulii ''%%perm(\[F | R\], P):- perm(R, P1), remove(F, P, P1).%%'' este, de fapt, de a insera elementul ''%%F%%'' în permutarea ''%%P1%%''. Poziția pe care va fi inserat elementul va fi diferită la fiecare resatisfacere a scopului ''%%remove(F, P, P1)%%'', ceea ce ne ajută sa obținem permutările. | + | </code> |
+ | Observați ca am definit predicatul ''%%remove(+Elem, +Lista, -ListaNoua)%%'', care șterge un element dintr-o listă. Rolul acestuia în cadrul regulii ''%%perm([F | R], P):- perm(R, P1), remove(F, P, P1).%%'' este, de fapt, de a insera elementul ''%%F%%'' în permutarea ''%%P1%%''. Poziția pe care va fi inserat elementul va fi diferită la fiecare resatisfacere a scopului ''%%remove(F, P, P1)%%'', ceea ce ne ajută sa obținem permutările. | ||
- | ''%%?- remove(3, L, [1, 1, 1]).%%'' ''%%L = [3, 1, 1, 1] ;%%'' ''%%L = [1, 3, 1, 1] ;%%'' ''%%L = [1, 1, 3, 1] ;%%'' ''%%L = [1, 1, 1, 3] ;%%'' ''%%false.%%'' | + | <code> |
+ | ?- remove(3, L, [1, 1, 1]). | ||
+ | L = [3, 1, 1, 1] ; | ||
+ | L = [1, 3, 1, 1] ; | ||
+ | L = [1, 1, 3, 1] ; | ||
+ | L = [1, 1, 1, 3] ; | ||
+ | false. | ||
- | ''%%?- perm([1, 2, 3], Perm).%%'' ''%%Perm = [1, 2, 3] ;%%'' ''%%Perm = [2, 1, 3] ;%%'' ''%%Perm = [2, 3, 1] ;%%'' ''%%Perm = [1, 3, 2] ;%%'' ''%%Perm = [3, 1, 2] ;%%'' ''%%Perm = [3, 2, 1] ;%%'' ''%%false.%%'' | + | </code> |
+ | <code> | ||
+ | ?- perm([1, 2, 3], Perm). | ||
+ | Perm = [1, 2, 3] ; | ||
+ | Perm = [2, 1, 3] ; | ||
+ | Perm = [2, 3, 1] ; | ||
+ | Perm = [1, 3, 2] ; | ||
+ | Perm = [3, 1, 2] ; | ||
+ | Perm = [3, 2, 1] ; | ||
+ | false. | ||
+ | </code> | ||
Putem folosi puterea generativă a limbajului pentru a produce soluții bazate pe combinații. De exemplu, dacă ne dorim toate perechile de numere (ca soluții succesive) din listele ''%%L1%%'' și ''%%L2%%'', dar a căror sumă //nu// este în lista ''%%L3%%'', putem folosi interogarea: | Putem folosi puterea generativă a limbajului pentru a produce soluții bazate pe combinații. De exemplu, dacă ne dorim toate perechile de numere (ca soluții succesive) din listele ''%%L1%%'' și ''%%L2%%'', dar a căror sumă //nu// este în lista ''%%L3%%'', putem folosi interogarea: | ||
- | ''%%[L1,L2,L3]=[[1,2,3], [4,5,6], [5,6]], member(X, L1), member(Y, L2), S is X + Y, \+ member(S, L3).%%'' | + | ''%%?- [L1,L2,L3]=[[1,2,3], [4,5,6], [5,6]], member(X, L1), member(Y, L2), S is X + Y, \+ member(S, L3).%%'' |
===== Obținerea de soluții prin generare și testare ===== | ===== Obținerea de soluții prin generare și testare ===== | ||
- | Fie problema colorării a 7 țări de pe o hartă folosind 3 culori. Scopul este acela de a atribui câte o culoare fiecărei țări, astfel încât nicio țară să nu aibă niciun vecin de aceeași culoare cu aceasta. Soluția problemei va fi o listă de atribuiri din domeniul ''%%\["r", "g", "b"\]%%'', care desemnează culorile atribuite fiecărei țări ''%%(1, 2, 3, 4, 5, 6, 7)%%''. | + | Fie problema colorării a 7 țări de pe o hartă folosind 3 culori. Scopul este acela de a atribui câte o culoare fiecărei țări, astfel încât nicio țară să nu aibă niciun vecin de aceeași culoare cu aceasta. Soluția problemei va fi o listă de atribuiri din domeniul ''%%["r", "g", "b"]%%'', care desemnează culorile atribuite fiecărei țări ''%%(1, 2, 3, 4, 5, 6, 7)%%''. |
Această strategie se traduce în următorul cod Prolog: | Această strategie se traduce în următorul cod Prolog: | ||
<code> | <code> | ||
- | % predicat care verifică că toate elementele din prima listă sunt prezente în a doua | + | % predicat care verifică că toate elementele din prima listă sunt prezente în a doua |
all_members([], _). | all_members([], _). | ||
- | all_members([X | Rest], In) :- member(X, In), all_members(Rest, In). | + | all_members([X | Rest], In) :- member(X, In), all_members(Rest, In). |
% predicat care verifică faptul că țările nu au culori identice cu niciun vecin | % predicat care verifică faptul că țările nu au culori identice cu niciun vecin | ||
Line 84: | Line 117: | ||
===== Backtracking atunci când cunoaștem dimensiunea soluției ===== | ===== Backtracking atunci când cunoaștem dimensiunea soluției ===== | ||
- | Mecanismul de backtracking ne oferă o rezolvare mai eficientă. Știm că orice soluție pentru problema colorării hărților constă într-o listă de atribuiri a 3 culori, de genul ''%%%%\[X1/C1,X2/C2, ... X7/C7\]%%%%'', scopul programului de rezolvare fiind să instanțieze adecvat variabilele X1, C1, X2, C2 etc. | + | Mecanismul de backtracking ne oferă o rezolvare mai eficientă. Știm că orice soluție pentru problema colorării hărților constă într-o listă de atribuiri a 3 culori, de genul ''%%[X1/C1,X2/C2, ... X7/C7]%%'', scopul programului de rezolvare fiind să instanțieze adecvat variabilele X1, C1, X2, C2 etc. |
- | Vom considera că orice soluție este de forma ''%%%%\[1/C1,2/C2, ... 7/C7\]%%%%'', deoarece ordinea țărilor nu este importantă. | + | Vom considera că orice soluție este de forma ''%%[1/C1,2/C2, ... 7/C7]%%'', deoarece ordinea țărilor nu este importantă. |
- | Fie problema mai generală a colorării a N țări folosind M culori. Definim soluția pentru N = 7 ca o soluție pentru problema generală a colorării hărților, care în plus respectă template-ul ''%%%%\[1/Y1,2/Y2, ... 7/Y7\]%%%%''. Semnul "/" este folosit în acest caz ca o modalitate de alipire a unor valori, fără a calcula vreodată împărțirea. | + | Fie problema mai generală a colorării a N țări folosind M culori. Definim soluția pentru N = 7 ca o soluție pentru problema generală a colorării hărților, care în plus respectă template-ul ''%%[1/Y1,2/Y2, ... 7/Y7]%%''. Semnul "/" este folosit în acest caz ca o modalitate de alipire a unor valori, fără a calcula vreodată împărțirea. |
În Prolog vom scrie: | În Prolog vom scrie: | ||
<code> | <code> | ||
- | %% Lungimea soluției este cunoscută și fixă. | + | % Lungimea soluției este cunoscută și fixă. |
- | template(\[1/\_, 2/\_, 3/\_, 4/\_, 5/\_, 6/\_, 7/\_\]). | + | template([1/_, 2/_, 3/_, 4/_, 5/_, 6/_, 7/_]). |
- | correct(\[\]) :- \!. correct(\[X/Y | Others\]):- | + | correct([]) :- !. |
- | correct(Others),` | + | correct([X/Y | Others]):- |
- | member(Y, ["r", "g", "b"]),` | + | correct(Others), |
- | safe(X/Y, Others).` | + | member(Y, ["r", "g", "b"]), |
+ | safe(X/Y, Others). | ||
- | solve\_maps(S):-template(S), correct(S). | + | solve_maps(S):-template(S), correct(S). |
</code> | </code> | ||
Line 111: | Line 145: | ||
==== Negația ca eșec (\+) ==== | ==== Negația ca eșec (\+) ==== | ||
- | ''%%\\+%%'' este operatorul folosit pentru negație în Prolog (meta-predicatul ''%%not%%'' nu mai este recomandat). Așa cum ați observat nu se pot adăuga în baza de date fapte în forma negată și nici nu se pot scrie reguli pentru acestea. Dacă nu se poate demonstra ''%%¬p%%'', atunci ce semnificație are în Prolog ''%%not(Goal)%%'' sau ''%%\\+ Goal%%''? | + | ''%%\+%%'' este operatorul folosit pentru negație în Prolog (meta-predicatul ''%%not%%'' nu mai este recomandat). Așa cum ați observat nu se pot adăuga în baza de date fapte în forma negată și nici nu se pot scrie reguli pentru acestea. Dacă nu se poate demonstra ''%%¬p%%'', atunci ce semnificație are în Prolog ''%%not(Goal)%%'' sau ''%%\+ Goal%%''? |
- | Prolog utilizează %%//%%presupunerea lumii închise%%//%%: ceea ce nu poate fi demonstrat, este fals. De aceea, în Prolog ''%%\\+ p%%'' trebuie citit ca "scopul ''%%p%%'' nu poate fi satisfăcut" sau "''%%p%%'' nu poate fi demonstrat". Faptul că Prolog utilizează negația ca eșec (eng. %%//%%negation as falseure%%//%%) are implicații asupra execuției programelor. | + | Prolog utilizează //presupunerea lumii închise//: ceea ce nu poate fi demonstrat, este fals. De aceea, în Prolog ''%%\+ p%%'' trebuie citit ca "scopul ''%%p%%'' nu poate fi satisfăcut" sau "''%%p%%'' nu poate fi demonstrat". Faptul că Prolog utilizează negația ca eșec (eng. //negation as failure//) are implicații asupra execuției programelor. |
În logica de ordinul întâi, următoarele două expresii sunt echivalente: ''%%¬a(X) & b(X)%%'' și ''%%b(X) & ¬a(X)%%''. În Prolog, următoarele 2 clauze (''%%p1%%'' și ''%%p2%%'') vor produce rezultate diferite: | În logica de ordinul întâi, următoarele două expresii sunt echivalente: ''%%¬a(X) & b(X)%%'' și ''%%b(X) & ¬a(X)%%''. În Prolog, următoarele 2 clauze (''%%p1%%'' și ''%%p2%%'') vor produce rezultate diferite: | ||
Line 119: | Line 153: | ||
<code> | <code> | ||
student(andrei). student(marius). lazy(marius). | student(andrei). student(marius). lazy(marius). | ||
- | p1(X) :- student(X), \\+ lazy(X). | + | p1(X) :- student(X), \+ lazy(X). |
- | p2(X) :- \\+ lazy(X), student(X). | + | p2(X) :- \+ lazy(X), student(X). |
</code> | </code> | ||
- | Acest lucru se întâmplă pentru că, în ''%%p2%%'', Prolog nu poate să derive, pe baza negației, legări pentru ''%%X%%''. În prolog putem folosi negația doar pentru a %%//%%verifica%%//%% variabile deja legate, sau pentru a exprima faptul că %%//%%nu se poate demonstra că predicatul este adevărat%%//%%. În ''%%p1%%'', ''%%X%%'' este legat și negația are rolul de a verifica că ''%%lazy%%'' nu este adevărat pentru ''%%X%%''. În ''%%p2%%'', ''%%X%%'' este nelegat, deci putem interpreta rezultatele folosind a doua modalitate: Prolog va încerca să demonstreze că nu există ''%%X%%'' pentru care ''%%lazy%%'' să fie adevărat, ceea ce nu este corect. | + | Acest lucru se întâmplă pentru că, în ''%%p2%%'', Prolog nu poate să derive, pe baza negației, legări pentru ''%%X%%''. În Prolog putem folosi negația doar pentru a //verifica// variabile deja legate, sau pentru a exprima faptul că //nu se poate demonstra că predicatul este adevărat//. În ''%%p1%%'', ''%%X%%'' este legat și negația are rolul de a verifica că ''%%lazy%%'' nu este adevărat pentru ''%%X%%''. În ''%%p2%%'', ''%%X%%'' este nelegat, deci putem interpreta rezultatele folosind a doua modalitate: Prolog va încerca să demonstreze că nu există ''%%X%%'' pentru care ''%%lazy%%'' să fie adevărat, ceea ce nu este corect. |
==== Predicatul false ==== | ==== Predicatul false ==== | ||
Line 135: | Line 169: | ||
</code> | </code> | ||
- | În codul de mai sus, am scris o regulă pentru funcția ''%%my\_reverse%%'' care are un singur rol: acela de a afișa argumentele ''%%List%%'' și ''%%Acc%%'' în orice moment se dorește satisfacerea unui scop cu predicatul ''%%my\_reverse/3%%''. Regula însă nu va avea efect asupra restului execuției, din moment ce ea eșuează întotdeauna. | + | În codul de mai sus, am scris o regulă pentru funcția ''%%my_reverse%%'' care are un singur rol: acela de a afișa argumentele ''%%List%%'' și ''%%Acc%%'' în orice moment se dorește satisfacerea unui scop cu predicatul ''%%my_reverse/3%%''. Regula însă nu va avea efect asupra restului execuției, din moment ce ea eșuează întotdeauna. |
<code> | <code> | ||
?- my_reverse([1,2,3,4],[],Rev). | ?- my_reverse([1,2,3,4],[],Rev). | ||
List:[1,2,3,4], Acc:[] | List:[1,2,3,4], Acc:[] | ||
- | List:[2,3,4], Acc:[1] | + | List:[2,3,4], Acc:[1] |
List:[3,4], Acc:[2,1] | List:[3,4], Acc:[2,1] | ||
List:[4], Acc:[3,2,1] | List:[4], Acc:[3,2,1] | ||
Line 147: | Line 181: | ||
</code> | </code> | ||
- | ==== Operatorul cut ==== | + | ==== Predicatul cut ==== |
- | În Prolog, operatorul cut (''%%\!%%'') are rolul de a elimina toate punctele de bifurcație create în predicatul curent. La evaluarea predicatului cut într-un predicat ''%%p%%'', se vor realiza două tipuri de efecte: | + | În Prolog, predicatul cut (''%%!%%'') are rolul de a elimina toate punctele de bifurcație create în predicatul curent. La evaluarea predicatului cut într-un predicat ''%%p%%'', se vor realiza două tipuri de efecte: |
- | * nu se vor mai genera soluții (dacă este nevoie, sau dacă soluția curent eșuează) pentru alte reguli ale predicatului ''%%p%%'' * nu se vor mai genera soluții (dacă este nevoie, sau dacă soluția curent eșuează), pentru alte soluții ale condițiilor care apar **în aceeași regulă cu cut**, și înainte de cut. | + | * nu se vor mai genera soluții (dacă este nevoie, sau dacă soluția curentă eșuează) pentru alte reguli ale predicatului ''%%p%%'' |
+ | * nu se vor mai genera soluții (dacă este nevoie, sau dacă soluția curentă eșuează), pentru alte soluții ale condițiilor care apar **în aceeași regulă cu cut**, și înainte de cut. | ||
De exemplu, în programul: | De exemplu, în programul: | ||
<code> | <code> | ||
- | p(a). | + | p(a). |
- | p(b). | + | p(b). |
- | p(A/B) :- q(A), \!, t(A/B). p(d). | + | p(A/B) :- q(A), !, t(A/B). |
+ | p(d). | ||
- | q(a). | + | q(a). |
- | q(b). | + | q(b). |
q(c). | q(c). | ||
- | t(a/a). | + | t(a/a). |
- | t(a/b). | + | t(a/b). |
- | t(b/c). | + | t(b/c). |
- | t(b/d). | + | t(b/d). |
</code> | </code> | ||
Line 181: | Line 217: | ||
Primele două soluții sunt soluții date de primele două reguli pentru ''%%p%%''. Pentru următoarea soluție, Prolog încearcă regula cu cut. Pentru ''%%q(A)%%'', sunt trei alternative: ''%%A=a, A=b, A=c%%''. La încercarea primeia, însă, urmează predicatul cut, care anulează: | Primele două soluții sunt soluții date de primele două reguli pentru ''%%p%%''. Pentru următoarea soluție, Prolog încearcă regula cu cut. Pentru ''%%q(A)%%'', sunt trei alternative: ''%%A=a, A=b, A=c%%''. La încercarea primeia, însă, urmează predicatul cut, care anulează: | ||
- | * alternativele pentru ''%%q%%'', deci nu se vor mai considera posibilitățile ''%%A=b%%'' și ''%%A=c%%''\\ | + | * alternativele pentru ''%%q%%'', deci nu se vor mai considera posibilitățile ''%%A=b%%'' și ''%%A=c%%'' |
- | * alternativele pentru ''%%p%%'', deci nu se va mai considera regula ''%%p(d)%%'' | + | * alternativele pentru ''%%p%%'', deci nu se va mai considera regula ''%%p(d)%%'' |
- | Alternativele pentru ''%%t%%'' sunt însă considerate normal, pentru că acestea se creează %%//%%după%%//%% evaluarea lui cut. | + | Alternativele pentru ''%%t%%'' sunt însă considerate normal, pentru că acestea se creează //după// evaluarea lui cut. |
- | Putem utiliza predicatul cut în două moduri: * atunci când știm că am ajuns la soluția care ne interesează, și știm că nu mai avem nevoie de o altă soluție pentru predicat, putem utiliza cut pentru a nu mai explora alte soluții (cut verde / %%//%%green cut%%//%%). * atunci când dorim în mod explicit ca Prolog să nu mai exploreze alte posibilități pentru același predicat, pentru că acestea nu ar genera soluții corecte, dacă se aplică regula curentă (cut roșu / %%//%%red cut%%//%%). | + | Putem utiliza predicatul cut în două moduri: |
- | Exemplu: implementarea predicatului ''%%min%%''. | + | * atunci când știm că am ajuns la soluția care ne interesează, și știm că nu mai avem nevoie de o altă soluție pentru predicat, putem utiliza cut pentru a nu mai explora alte soluții (cut verde / //green cut//). |
+ | * atunci când dorim în mod explicit ca Prolog să nu mai exploreze alte posibilități pentru același predicat, pentru că acestea nu ar genera soluții corecte, dacă se aplică regula curentă (cut roșu / //red cut//). | ||
- | Varianta 1 -- fără cut: | + | **Exemplu:** implementarea predicatului ''%%min%%''. |
+ | |||
+ | **Varianta 1** -- fără cut: | ||
<code> | <code> | ||
- | min(X, Y, Min) :- X \< Y, X = Min. % regula 1 | + | min(X, Y, Min) :- X < Y, X = Min. % regula 1 |
- | min(X, Y, Min) :- X \>= Y, Y = Min. % regula 2 | + | min(X, Y, Min) :- X >= Y, Y = Min. % regula 2 |
</code> | </code> | ||
- | Este important să avem comparația din regula 2, în lipsa ei obținând, pentru cazul în care ''%%X \< Y%%'', o a doua soluție falsă, în care Y este minimul. Testați cu: | + | Este important să avem comparația din regula 2, în lipsa ei obținând, pentru cazul în care ''%%X < Y%%'', o a doua soluție falsă, în care Y este minimul. Testați cu: |
<code> | <code> | ||
- | minB(X, Y, Min) :- X \< Y, X = Min. % regula 1 | + | minB(X, Y, Min) :- X < Y, X = Min. % regula 1 |
- | minB(\_, Y, Min) :- Y = Min. % regula 2 | + | minB(_, Y, Min) :- Y = Min. % regula 2 |
</code> | </code> | ||
Pentru interogarea ''%%minB(2, 3, Min)%%'' se obțin două soluții: ''%%Min=2%%'' și ''%%Min=3%%''. | Pentru interogarea ''%%minB(2, 3, Min)%%'' se obțin două soluții: ''%%Min=2%%'' și ''%%Min=3%%''. | ||
- | Putem integra predicatul cut ca un cut verde astfel: | + | **Varianta 2** Putem integra predicatul cut ca un cut verde astfel: |
<code> | <code> | ||
- | min2(X, Y, Min) :- X \< Y, \!, X = Min. % regula 1 | + | min2(X, Y, Min) :- X < Y, !, X = Min. % regula 1 |
- | min2(X, Y, Min) :- X \>= Y, Y = Min. % regula 2 | + | min2(X, Y, Min) :- X >= Y, Y = Min. % regula 2 |
</code> | </code> | ||
- | Este o utilizare de tip "green cut" - cut poate să lipsească și execuția nu devine incorectă, dar este îmbunătățită prin faptul că atunci când ''%%X \< Y%%'' Prolog va ști să nu mai intre (nu mai este nevoie să intre) pe a doua regulă. | + | Este o utilizare de tip "green cut" - cut poate să lipsească și execuția nu devine incorectă, dar este îmbunătățită prin faptul că atunci când ''%%X < Y%%'' Prolog va ști să nu mai intre (nu mai este nevoie să intre) pe a doua regulă. |
Observați că este important ca predicatul cut să fie pus atunci când știm cu siguranță ca suntem pe ramura corectă. Dacă, de exemplu, implementăm astfel: | Observați că este important ca predicatul cut să fie pus atunci când știm cu siguranță ca suntem pe ramura corectă. Dacă, de exemplu, implementăm astfel: | ||
<code> | <code> | ||
- | min2B(X, Y, Min) :- \!, X \< Y, X = Min. | + | min2B(X, Y, Min) :- !, X < Y, X = Min. |
- | min2B(X, Y, Min) :- X \>= Y, Y = Min. | + | min2B(X, Y, Min) :- X >= Y, Y = Min. |
</code> | </code> | ||
Pentru ''%%min2B(3, 2 ,M)%%'', se va evalua predicatul cut (care anulează alternativa pentru min2B), inegalitatea eșuează, și interogarea va eșua, pentru că din cauza lui cut Prolog nu mai intră și pe a doua regulă. | Pentru ''%%min2B(3, 2 ,M)%%'', se va evalua predicatul cut (care anulează alternativa pentru min2B), inegalitatea eșuează, și interogarea va eșua, pentru că din cauza lui cut Prolog nu mai intră și pe a doua regulă. | ||
- | Putem integra predicatul cut ca un cut roșu astfel: | + | **Varianta 3** Putem integra predicatul cut ca un cut roșu astfel: |
<code> | <code> | ||
- | min3(X, Y, Min) :- X \< Y, \!, X = Min. | + | min3(X, Y, Min) :- X < Y, !, X = Min. |
- | min3(\_, Y, Min) :- Y = Min. | + | min3(_, Y, Min) :- Y = Min. |
</code> | </code> | ||
Dacă inegalitatea din prima regulă reușește, sigur nu mai avem nevoie de a doua regulă. | Dacă inegalitatea din prima regulă reușește, sigur nu mai avem nevoie de a doua regulă. | ||
- | Aici, cut //nu// poate să lipsească -- dacă lipsește vom obține și soluții incorecte, ca în cazul lui ''%%minB%%''. | + | Aici, cut **nu** poate să lipsească -- dacă lipsește vom obține și soluții incorecte, ca în cazul lui ''%%minB%%''. |
+ | |||
+ | ===== Resurse ===== | ||
+ | |||
+ | * [[https://ocw.cs.pub.ro/courses/_media/pp/21/laboratoare/prolog/legare-skel.zip|Schelet]] | ||
+ | * [[https://ocw.cs.pub.ro/courses/_media/pp/21/laboratoare/prolog/legare-sol.zip|Soluții]] | ||