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 11:41]
dmihai [Recommended Reading]
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 ====
  
-=== Green cut ===+Pentru a demonstra goal-urile necesare, Prolog folosește backtracking pentru a explora toate posibilitățile,​ rezultând într-un //arbore de căutare//. Uneori asta duce la comportamente neintuitive:​
  
-=== Red cut ===+<code prolog>​ 
 +min(X, Y, X) :- X < Y. 
 +min(X, Y, Y). 
 +</​code>​ 
 + 
 +<​code>​ 
 +?- 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 ; 
 +false. 
 + 
 +?- 
 +</​code>​ 
 + 
 +Putem porni un trace în program pentru a vedea ce se întâmplă:​ 
 + 
 +<​code>​ 
 +?- trace. 
 + 
 +[trace] ​ ?- min(1, 2, 1). 
 +   Call: (8) min(1, 2, 1) ? creep 
 +   Call: (9) 1<2 ? creep 
 +   Exit: (9) 1<2 ? creep 
 +   Exit: (8) min(1, 2, 1) ? creep 
 +true ; 
 +   Redo: (8) min(1, 2, 1) ? creep 
 +   Fail: (8) min(1, 2, 1) ? creep 
 +false. 
 + 
 +[trace] ​ ?- 
 +</​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 ''​min(X,​ Y, 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ă 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>​ 
 +min(X, Y, X) :- X < Y, !. 
 +min(X, Y, Y) :- Y >X. 
 +</​code>​ 
 + 
 +<​code>​ 
 +[trace] ​ ?- min(1, 2, 1). 
 +   Call: (8) min(1, 2, 1) ? creep 
 +   Call: (9) 1<2 ? creep 
 +   Exit: (9) 1<2 ? creep 
 +   Exit: (8) min(1, 2, 1) ? creep 
 +true. 
 + 
 +[trace] ​ ?- 
 +</​code>​ 
 + 
 +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''​. 
 + 
 +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"​. 
 + 
 +=== Green cuts === 
 + 
 +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. 
 + 
 +=== 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''​.
  
-==== Negation as failure ====+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.
  
-==== Colectare rezultate ​====+==== Colectarea rezultatelor ​====
  
 Fie următoarea bază de cunoștiințe genealogice:​ Fie următoarea bază de cunoștiințe genealogice:​
Line 128: 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 199: 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 ====
  
-[[https://​mitpress.mit.edu/​books/​art-prolog-second-edition|The Art of Prolog - II. The Prolog Language - 11. Cuts and Negation +  * [[https://​mitpress.mit.edu/​books/​art-prolog-second-edition|The Art of Prolog - II. The Prolog Language - 11. Cuts and Negation]] 
-[[http://​www.learnprolognow.org/​lpnpage.php?​pagetype=html&​pageid=lpn-htmlch5|Learn Prolog Now! - Chapter 5 Arithmetic]] +  ​* ​[[http://​www.learnprolognow.org/​lpnpage.php?​pagetype=html&​pageid=lpn-htmlch5|Learn Prolog Now! - Chapter 5 Arithmetic]] 
-[[http://​www.learnprolognow.org/​lpnpage.php?​pagetype=html&​pageid=lpn-htmlch6|Learn Prolog Now! - Chapter 6 More Lists]] +  ​* ​[[http://​www.learnprolognow.org/​lpnpage.php?​pagetype=html&​pageid=lpn-htmlch6|Learn Prolog Now! - Chapter 6 More Lists]] 
-[[http://​www.learnprolognow.org/​lpnpage.php?​pagetype=html&​pageid=lpn-htmlch10|Learn Prolog Now! - Chapter 10 Cuts and Negation]] +  ​* ​[[http://​www.learnprolognow.org/​lpnpage.php?​pagetype=html&​pageid=lpn-htmlch10|Learn Prolog Now! - Chapter 10 Cuts and Negation]] 
-[[http://​www.learnprolognow.org/​lpnpage.php?​pagetype=html&​pageid=lpn-htmlch11|Learn Prolog Now! - Chapter 11 Database Manipulation and Collecting Solutions]]+  ​* ​[[http://​www.learnprolognow.org/​lpnpage.php?​pagetype=html&​pageid=lpn-htmlch11|Learn Prolog Now! - Chapter 11 Database Manipulation and Collecting Solutions]]