Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
pp:lplcut [2019/05/06 12:33]
dmihai [Cut]
pp:lplcut [2019/05/06 14:30] (current)
dmihai [Colectarea rezultatelor]
Line 1: Line 1:
-===== TODO =====+===== Cuts =====
  
 Scopul laboratorului:​ Scopul laboratorului:​
  
-  - TODO +  - aprofundarea mecanismului de căutare din prolog 
-  - TODO+  - înțelegerea cut-urilor ca mecanism de control al căutării 
 +  - distincția între "green cuts" și "red cuts"​ 
 +  - metode de colectare a rezultatelor
  
 ==== Cut ==== ==== Cut ====
Line 11: Line 13:
  
 <code prolog> <code prolog>
-elem(X, [X|_]). +min(X, Y, X) :- X < Y
-elem(X, [_|Ys]) :- member(XYs).+min(X, YY).
 </​code>​ </​code>​
  
 <​code>​ <​code>​
-?- elem(1, [1]).+?- min(1, 2, 1). 
 +true ; 
 +false. 
 + 
 +?- 
 +</​code>​ 
 + 
 +Putem încerca să rezolvăm problema, explicitând condiția din cea de-a doua clauză: 
 +<code prolog>​ 
 +min(X, Y, X) :- X < Y. 
 +min(X, Y, Y) :- X >= Y. 
 +</​code>​ 
 + 
 +Dar tot obținem un al doilea rezultat ''​false''​. 
 + 
 +<​code>​ 
 +?- min(1, 2, 1).
 true ; true ;
 false. false.
Line 28: Line 46:
 ?- trace. ?- trace.
  
-[trace] ​ ?- elem(1, [1]). +[trace] ​ ?- min(1, 2, 1). 
-   Call: (8) elem(1, [1]) ? creep +   Call: (8) min(1, 2, 1) ? creep 
-   Exit: (8) elem(1, [1]) ? creep+   Call: (9) 1<2 ? creep 
 +   Exit: (9) 1<​2 ​? creep 
 +   Exit: (8) min(1, 2, 1) ? creep
 true ; true ;
-   Redo: (8) elem(1, [1]) ? creep +   Redo: (8) min(1, 2, 1) ? creep 
-   Call: (9) elem(1[]) ? creep +   Fail: (8) min(1, 2, 1) ? creep
-   Fail: (9) elem(1, []) ? creep +
-   Fail: (8) elem(1, [1]) ? creep+
 false. false.
  
Line 41: Line 59:
 </​code>​ </​code>​
  
-După ce obținem un răspuns positiv din prima clauză, Prolog încearcă să găsească și alte soluții, folosind a doua ramură. Ajunge astfel la goal-ul ''​elem(1[])''​ care nu poate fi satisfăcut,​ deci căutarea eșuează. Am vrea să avem control asupra execuției procedurale,​ eliminând ramuri din arborele de căutare (în cazul nostru, am vrea ca după ce am aflat că un element se găsește în listă, să ne oprim).+După ce obținem un răspuns positiv din prima clauză, Prolog încearcă să găsească și alte soluții, folosind a doua ramură. Ajunge astfel la goal-ul ''​min(XY, Y)''​ care nu poate fi satisfăcut, deoarece nu există o unificare posibilă, deci căutarea eșuează. Am vrea să avem control asupra execuției procedurale,​ eliminând ramuri din arborele de căutare (în cazul nostru, am vrea ca după ce am aflat că un element se găsește în listă, să ne oprim).
  
-Pentru asta avem predicatul [[http://​www.swi-prolog.org/​pldoc/​doc_for?​object=!/​0|!/​0]] (numit "​cut"​). Acesta poate fi mereu satisfăcut,​ dar are un efect lateral: împiedică resatisfacerea părții din stânga, inclusiv **clauza** (amintiți-vă că o clauză estepractic o "​linie",​ iar un predicat e o colecție de una sau mai multe clauze. Predicatul ''​elem''​ are două clauze). Astfel, putem rezolva problema noastră:+Pentru asta avem predicatul [[http://​www.swi-prolog.org/​pldoc/​doc_for?​object=!/​0|!/​0]] (numit "​cut"​). Acesta poate fi mereu satisfăcut,​ dar are un efect lateral: împiedică resatisfacerea părții din stânga, inclusiv **clauza** (amintiți-vă că o clauză este practic o "​linie",​ iar un predicat e o colecție de una sau mai multe clauze. Predicatul ''​elem''​ are două clauze). Astfel, putem rezolva problema noastră:
  
 <code prolog> <code prolog>
-elem(X, [X|_]) :- !. +min(X, Y, X) :- X < Y, !. 
-elem(X, [_|Ys]) :- member(X, Ys).+min(X, Y, Y) :- Y >= X.
 </​code>​ </​code>​
  
 <​code>​ <​code>​
-[trace] ​ ?- elem(1, [1]). +[trace] ​ ?- min(1, 2, 1). 
-   Call: (8) elem(1, [1]) ? creep +   Call: (8) min(1, 2, 1) ? creep 
-   Exit: (8) elem(1, [1]) ? creep+   Call: (9) 1<2 ? creep 
 +   Exit: (9) 1<​2 ​? creep 
 +   Exit: (8) min(1, 2, 1) ? creep
 true. true.
  
Line 59: Line 79:
 </​code>​ </​code>​
  
-Când primește goal-ul ''​elem(1, [1])'',​ prolog încearcă, de sus în jos, toate clauzele predicatului ''​elem''​. Pentru că reușește unificarea ​pe prima clauză (cu substituția ''​X = 1''​), alege prima clauză. Aceasta este o regulă, deci trebuie încercată satisfacerea corpului acesteia, adică ''​!''​. ​Predicatul ​''​!''​ reușește și deci și clauza curentă, iar prolog ne raportează ''​true'';​ dar acum nu mai poate să încerce o altă clauză a predicatului ''​elem''​.+Când primește goal-ul ''​min(1, 2, X)'',​ prolog încearcă, de sus în jos, toate clauzele predicatului ''​min''​. Pentru că reușește unificarea cu substituția ''​{X = 1, Y = 2}'',​ alege prima clauză. Aceasta este o regulă, deci trebuie încercată satisfacerea corpului acesteia, adică ''​X < Y, !''​. ​''​1 < 2''​ este adevărat, iar predicatul ​''​!''​ reușește și deci și clauza curentă, iar prolog ne raportează ''​true'';​ dar acum nu mai poate să încerce o altă clauză a predicatului ''​min''​.
  
-=== Green cut ===+Putem folosi predicatul "cut" pentru a altera rezultatul unei interogări,​ dar și doar cu scopul de a face o interogare mai eficientă, având același rezultat. Astfel distingem între "green cuts" și "red cuts".
  
-=== Red cut ===+=== Green cuts ===
  
-==== Negation as failure ====+Un "green cut" este un "​cut"​ care are scopul de a face un program mai eficient, fără a-i schimba outputul. Cut-ul folosit mai sus are această proprietate. Fără acesta, prolog ar verifica și cea de-a doua ramură, în mod inutil. În principal, un "green cut" ar trebui folosit pentru clause mutual-exclusive:​ dacă una din ele e adevărate, cealălalte nu poate fi. O carecteristică a acestor cut-uri este că eliminarea lor din program, nu schimbă rezultatul.
  
-==== Colectare rezultate ​====+=== Red cuts === 
 + 
 +Definiția predicatului ''​min''​ de mai sus, poate părea nesatisfăctoare. Avem în ambele clauzele o comparație explicită a argumentelor. Intuitiv, observăm că cele două condiții sunt mutual exclusive, deci ar fi suficient ca doar una să fie explicitată. 
 + 
 +<code prolog>​ 
 +min(X, Y, X) :- X < Y, !. 
 +min(X, Y, Y). 
 +</​code>​ 
 + 
 +La prima vedere, predicatul pare corect: 
 + 
 +<​code>​ 
 +?- min(1, 2, X). 
 +X = 1. 
 + 
 +?- min(2, 1, X). 
 +X = 1. 
 + 
 +?- 
 +</​code>​ 
 + 
 +Însă putem observa că atunci când toate variabilele sunt instanțiate,​ comportamentul nu e cel dorit: 
 + 
 +<​code>​ 
 +?- min(1, 2, 2). 
 +true. 
 + 
 +?- 
 +</​code>​ 
 + 
 +Pentru că nu poate aborda prima clauză (nu există nicio substituție posibilă, ''​X''​ ar trebui să fie și ''​1''​ și ''​2''​),​ prolog trece la a doua. Cu substituția ''​{X = 1, Y = 2}'',​ goal-ul reușește și prolog raportează ''​true''​. 
 + 
 +O soluție este să întârziem unificarea variablei rezultat, până după comparație:​ 
 + 
 +<code prolog>​ 
 +min(X, Y, Z) :- X < Y, !, X = Z. 
 +min(X, Y, Y). 
 +</​code>​ 
 + 
 +<​code>​ 
 +?- min(1, 2, 2). 
 +false. 
 + 
 +?- 
 +</​code>​ 
 + 
 +Inițial prolog găsește substituția ''​{X = 1, Y = 2, Z = 2}''​. Comparația ''​X < Y''​ reușește (deoarece ''​1 < 2''​),​ ''​!''​ reușește, dar unificarea ''​X = Z''​ nu (ambele sunt variabile instanțiate la valori diferite). Din cauza cut-ului, prolog nu poate să încerce a doua clauză, deci rezultatul e ''​false''​. 
 + 
 +Fără acel cut, predicatul ar funcționa ca variante dinainte (cea fără variabila ''​Z''​). Astfel de cut-uri, care alterează comportamentul programului,​ se numesc "red cuts". O recomandare comună e că ar trebui folosite cu grijă, pentru că pot îngreuna înțelegerea codului. 
 + 
 +==== Colectarea rezultatelor ​====
  
 Fie următoarea bază de cunoștiințe genealogice:​ Fie următoarea bază de cunoștiințe genealogice:​
Line 181: Line 251:
 ?- ?-
 </​code>​ </​code>​
- 
-<​note>​ 
-În ultima interogare, nu contează dacă predicatul folosit în exterior este ''​bagof/​3''​ sau ''​findall/​3''​. Goal-ul de demonstrat (''​bagof(C,​ parent(P, C), L)''​) nu conține variabile "din afară"​. 
-</​note>​ 
  
 Ca urmare a felului de funcționare,​ o diferență notabilă între ''​findall/​3''​ și ''​bagof/​3''​ este că, atunci când goal-ul nu poate fi satisfăcut,​ ''​findall/​3''​ reușește, generând o listă vidă, iar ''​bagof/​3''​ eșuează. Ca urmare a felului de funcționare,​ o diferență notabilă între ''​findall/​3''​ și ''​bagof/​3''​ este că, atunci când goal-ul nu poate fi satisfăcut,​ ''​findall/​3''​ reușește, generând o listă vidă, iar ''​bagof/​3''​ eșuează.
Line 252: Line 318:
 ?- ?-
 </​code>​ </​code>​
 +
 +<​note>​
 +În mod default, swi-prolog trunchiază listele rezultat, afișând doar primele elemente și ''​...''​ pentru restul. Pentru a schimba acest comportament folosiți interogarea ''​set_prolog_flag(answer_write_options,​[max_depth(0)]).'':​
 +
 +<​code>​
 +?- findall(P, parent(P, C), L).
 +L = [bestla, bestla, bestla, jorth, frigg, buri, borr, borr, borr|...].
 +
 +?- set_prolog_flag(answer_write_options,​[max_depth(0)]).
 +true.
 +
 +?- findall(P, parent(P, C), L).
 +L = [bestla,​bestla,​bestla,​jorth,​frigg,​buri,​borr,​borr,​borr,​odin,​odin].
 +</​code>​
 +
 +Soluție alternativă găsiți [[https://​stackoverflow.com/​a/​36948699|aici]].
 +</​note>​
 +
 ==== Exerciții ==== ==== Exerciții ====
 +
 +  - Definiți predicatul ''​cartesian(L1,​ L2, R)''​ care construiește [[http://​mathworld.wolfram.com/​CartesianProduct.html|produsul cartezian]] al ''​L1''​ cu ''​L2''​.
 +  - Definiți predicatul ''​union(L1,​ L2, R)''​ care construiește reuniunea a două mulțimi codificate ca liste.
 +  - Definiți predicatul ''​intersection(L1,​ L2, R)''​ care construiește interesecția a două mulțimi codificate ca liste.
 +  - Definiți predicatul ''​diff(L1,​ L2, R)''​ care construiește diferenta a două mulțimi codificate ca liste.
 +  - Definiți predicatul ''​pow(S,​ R)''​ care construiește [[http://​mathworld.wolfram.com/​PowerSet.html|power set-ul]] multimii ''​S''​.
 +  - Definiți predicatul ''​perm(S,​ R)''​ care generează toate permutările lui ''​S''​.
 +  - Definiți predicatul ''​ar(K,​ S, R)''​ care generează toate aranjamentele de dimensiune ''​K''​ cu elemente luate din ''​S''​.
 +  - Definiți predicatul ''​comb(K,​ S, R)''​ care generează toate combinările de dimensiune ''​K''​ cu elemente luate din ''​S''​.
  
 ==== Recommended Reading ==== ==== Recommended Reading ====