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:2023:hw3 [2023/05/02 17:35]
pdmatei
pp:2023:hw3 [2023/05/06 15:56] (current)
alexandra.udrescu01 typo
Line 2: Line 2:
  
 <​note>​ <​note>​
-**TODO** Adauga schelet \\ +Schelet: {{:​pp:​2023:​tema3.zip|}}
-**TODO** Adauga validatorul de arhive+
 </​note>​ </​note>​
  
 <note important>​ <note important>​
 **Deadline:​** 28 mai, 23:59 **Deadline:​** 28 mai, 23:59
-  * Temele trebuie submise pe [[curs.upb.ro]], ​sub assignment-ul ''​Tema 3''​.+  * Temele trebuie submise pe [[curs.upb.ro]], ​în assignment-ul ''​Tema 3''​.
   * Temele care nu sunt acceptate de validatorul de arhive **NU** vor fi punctate.   * Temele care nu sunt acceptate de validatorul de arhive **NU** vor fi punctate.
   * Pentru întrebări folosiți forum-ul dedicat de pe [[curs.upb.ro]].   * Pentru întrebări folosiți forum-ul dedicat de pe [[curs.upb.ro]].
Line 34: Line 33:
  
 Înainte de a reduce o expresie (realizarea $\beta$-reducerii),​ trebuie să rezolvăm //​**coliziunile de nume**//. \\ Înainte de a reduce o expresie (realizarea $\beta$-reducerii),​ trebuie să rezolvăm //​**coliziunile de nume**//. \\
-Dacă am încerca să reducem un **redex** fără a face substituții textuale (un **redex** e o expresie reductibilă,​ **i.e.** are forma $( \lambda x.e_1 \ e_2 $) există riscul de a pierde întelesul original al expresie. \\ +Dacă am încerca să reducem un **redex** fără a face substituții textuale (un **redex** e o expresie reductibilă,​ **i.e.** are forma $( \lambda x.e_1 \ e_2 $) există riscul de a pierde întelesul original al expresiei. \\ 
-Spre exemplu **redex**-ul:​ $\lambda x.\lambda y.(x \ y) \ \lambda x.y$, ar fi redus la: $\lambda y.(\lambda x.y \ y)$. \\ +Spre exemplu **redex**-ul:​ $(\lambda x.\lambda y.(x \ y) \ \lambda x.y)$, ar fi redus la: $\lambda y.(\lambda x.y \ y)$. Acest efect nedorit are denumirea intuitivă de //​variable-capture//:​ Variabila inițial liberă $math[y] a devenit legată dupa reducere. \\ 
-Dar puteți observa că acestă expresie ​și-a pierdut sensul original, pentru că **y**-ul liber din $\lambda x.y$ e acum //bound// de $\lambda y.$ din expresia în care a fost înlocuit. \\ +Puteți observa că expresia ​și-a pierdut sensul original, pentru că **y**-ul liber din $\lambda x.y$ e acum //bound// de $\lambda y.$ din expresia în care a fost înlocuit. \\ 
-Astfel expresia corecta ar fi: $\lambda a.(\lambda x.y \ a)$. \\+Astfelexpresia corecta ar fi: $\lambda a.(\lambda x.y \ a)$. \\
 \\ \\
-**1.1.** Implementați funcția auxiliară ''​free_vars''​ care returnează o listă cu toate ''​String''​-urile care reprezintă variabile libere într-o expresie. (**notă**: dacă o variabilă este liberă în expresie în mai multe contexte, o să apară o singură dată în listă). \\ +**1.1.** ​(//​10p//​) ​Implementați funcția auxiliară ''​free_vars''​ care returnează o listă cu toate ''​String''​-urile care reprezintă variabile libere într-o expresie. (**notă**: dacă o variabilă este liberă în expresie în mai multe contexte, o să apară o singură dată în listă). \\ 
-**1.2.** Implementați funcția ''​reduce''​ care reduce un **redex** luând în considerare și //​**coliziunile de nume**//. Funcția primește **redex**-ul '​deconstruit'​ și returnează expresia rezultată. \\+**1.2.** ​(//​30p//​) ​Implementați funcția ''​reduce''​ care reduce un **redex** luând în considerare și //​**coliziunile de nume**//. Funcția primește **redex**-ul '​deconstruit'​ și returnează expresia rezultată. \\
 <code haskell> <code haskell>
 reduce :: Expr -> String -> Expr -> Expr reduce :: Expr -> String -> Expr -> Expr
Line 52: Line 51:
 O să facem reducerea „step by step”, implementăm o funcție care reduce doar următorul **redex** comform unei strategii. Apoi aplicăm acesți pași până expresia rămasă este în formă normală. Pentru asta o să implementați 2 funcții, una care returnează doar forma normală a expresiei, și una care returnează o listă cu toate formele intermediare (rezultatul după fiecare pas de reducere). \\ O să facem reducerea „step by step”, implementăm o funcție care reduce doar următorul **redex** comform unei strategii. Apoi aplicăm acesți pași până expresia rămasă este în formă normală. Pentru asta o să implementați 2 funcții, una care returnează doar forma normală a expresiei, și una care returnează o listă cu toate formele intermediare (rezultatul după fiecare pas de reducere). \\
 \\ \\
-**1.3.** Implementați funcția ''​stepN''​ care aplică un pas de reducere după strategia Normală. \\ +**1.3.** ​(//​5p//​) ​Implementați funcția ''​stepN''​ care aplică un pas de reducere după strategia Normală. \\ 
-**1.4.** Implementați funcțiile ''​reduceN''​ și ''​reduceAllN''​ care fac reducerea la forma normală folosind strategia Normală. \\ +**1.4.** ​(//​10p//​) ​Implementați funcțiile ''​reduceN''​ și ''​reduceAllN''​ care fac reducerea la forma normală folosind strategia Normală. \\ 
-**1.5.** Implementați funcția ''​stepA''​ care aplică un pas de reducere după strategia Aplicativă. \\ +**1.5.** ​(//​5p//​) ​Implementați funcția ''​stepA''​ care aplică un pas de reducere după strategia Aplicativă. \\ 
-**1.6.** Implementați funcțiile ''​reduceA''​ și ''​reduceAllA''​ care fac reducerea la formă normală folosind strategia Aplicativă. \\+**1.6.** ​(//​10p//​) ​Implementați funcțiile ''​reduceA''​ și ''​reduceAllA''​ care fac reducerea la formă normală folosind strategia Aplicativă. \\
  
 <note important>​ <note important>​
Line 67: Line 66:
 O gramatică pentru expresii lambda ar putea fi: \\ O gramatică pentru expresii lambda ar putea fi: \\
 <​code>​ <​code>​
-<​expr>​ ::= <​variable>​ | \.<​variable>​ <​expr>​ | <​expr>​ <​expr>​ | (<​expr>​)+<​expr>​ ::= <​variable>​ | '\' ​<​variable> ​'​.' ​<​expr>​ | <​expr>​ <​expr>​ | '(' ​<​expr> ​')'
 <​variable>​ ::= '​a'​ | '​b'​ | '​c'​ | ... | '​z'​ <​variable>​ ::= '​a'​ | '​b'​ | '​c'​ | ... | '​z'​
 </​code>​ </​code>​
 \\ \\
-Această gramatică exprimă corect ​expresiile ​lambda, dar nu este practică pentru o implementare,​ pentru că există termeni care nu fac progresDacă expresia ​care o parsăm este o aplicație, o să '​cicleze',​ pentru că o să intre pe termenul ​''<​expr>:​:=<​expr>​ <​expr>'' ​și o să încerce iar să parseze o ''<​expr>''​ din același ''​String''​nefăcând progres ​de la pasul anterior\\+Această gramatică exprimă corect ​structura expresiilor ​lambda, dar nu este practică pentru o implementare,​ pentru că procesul de parsare se poate bloca intr-o bucla infinitaSa presupunem ca expresia ​noastra ​''<​expr>​''​ este: 
 +<​code>​ "\x.x \y.y" </​code>​ 
 +Urmarind regulile de parsare de mai sus, un parser ar putea sa incerce sa aplice regula descrisa de ''​<​expr>​ <​expr>''​, caz in care un nou parser de expresii de tip ''<​expr>'' ​(pentru sub-expresia ​din stanga) va fi invocat. Absenta **progresului** (nici un sir de la input nu a fost consumat)va conduce procesul ​de parsare intr-o bucla infinita. 
 Pentru o parsare corectă și eficientă trebuie să definești o gramatică care face progres la fiecare pas. \\ Pentru o parsare corectă și eficientă trebuie să definești o gramatică care face progres la fiecare pas. \\
 +** Alege o organizare a gramaticii pentru lambda-expresii de asa maniera ca la fiecare pas, o portiune din sir sa fie consumata**.
  
 +/*
 O gramatică care face progres și o puteți folosi la temă este: O gramatică care face progres și o puteți folosi la temă este:
 <​code>​ <​code>​
Line 81: Line 85:
 <​variable>​ ::= '​a'​ | '​b'​ | '​c'​ | ... | '​z'​ <​variable>​ ::= '​a'​ | '​b'​ | '​c'​ | ... | '​z'​
 </​code>​ </​code>​
 +*/
  
-**2.1.** Implementați funcția ''​parse_expr''​ care parsează un ''​String''​ și returnează o expresie.+**2.1.** ​(//​50p//​) ​Implementați funcția ''​parse_expr''​ care parsează un ''​String''​ și returnează o expresie
 + 
 +<note important>​ 
 +**NU** aveți voie să schimbați structura parserului, o soluție care nu se folosește de tipul de date ''​Parser''​ din schelet, nu o să fie punctată pentru cerințele de parsare 
 +</​note>​ 
 + 
 +<note warning>​ 
 +Parserul care trebuie să îl implementați are definiția:​ 
 + 
 +<code haskell>​ 
 +newtype Parser a = Parser { 
 +    parse :: String -> Maybe(a, String) 
 +
 +</​code>​ 
 + 
 +Obervați că tipul care îl întoarce funcția de parsare este ''​Maybe(a,​ String)'',​ el întoarce ''​Nothing''​ dacă nu a putut parsa expresia sau ''​Just (x, s)''​ dacă a parsat ''​x'',​ iar din String-ul original a rămas sufix-ul ''​s''​.
  
-<note tip> 
-Implementarea funcției ''​parse_expr''​ este la latitudinea voastră, însă noi recomandăm folosirea monadei **Parse** prezentată la curs. Pentru asta aveți definit în schelet o instantă **Monad** (unde puteți să completați funcțiile ''​return''​ și ''>>​=''​),​ alături de instanțe de **Applicative** și **Functor**,​ necesare pentru a defini o monadă în //​Haskell//​. 
 </​note>​ </​note>​
  
Line 104: Line 122:
 Pentru a evalua o expresie cu macro-uri, introducem noțiunea de //**context computațional**//​. Contextul în care evaluăm o expresie este pur și simplu un dicționar de nume de macro-uri și expresii pe care aceste nume le înlocuiesc. Astfel când evaluăm un macro, facem pur și simpu substituție textuală cu expresia găsită în dicționar. Pentru a evalua o expresie cu macro-uri, introducem noțiunea de //**context computațional**//​. Contextul în care evaluăm o expresie este pur și simplu un dicționar de nume de macro-uri și expresii pe care aceste nume le înlocuiesc. Astfel când evaluăm un macro, facem pur și simpu substituție textuală cu expresia găsită în dicționar.
  
-**3.1.** Implementați funcția ''​evalMacros''​ care ia un context și o expresie care poate să conțină macro-uri, și întoarce expresia după evaluarea tuturor macro-urilor.+**3.1.** ​(//​10p//​) ​Implementați funcția ''​evalMacros''​ care ia un context și o expresie care poate să conțină macro-uri, și întoarce expresia după evaluarea tuturor macro-urilor.
  
-**3.2.** Modificați parser-ul vostru astfel încât să parsați și expresii care conțin macro-uri.+**3.2.** ​(//​5p//​) ​Modificați parser-ul vostru astfel încât să parsați și expresii care conțin macro-uri.
  
 <note tip> <note tip>
Line 128: Line 146:
 </​code>​ </​code>​
  
-**4.1.** Implementați funcția ''​evalCode''​ care primește o strategie de evaluare și o lista de linii de cod, și întoarce o listă cu rezultatele tuturor liniilor de tipul ''​Evaluate''​. +Un exemplu de cod care l-am putea scrie ar fi: 
-**4.2.** Implementați funcția ''​parse_code''​ care să parsează o linie de cod.+ 
 +$ true = \lambda x.\lambda y.x $ 
 + 
 +$ false = \lambda x.\lambda y.y $ 
 + 
 +$ and = \lambda x.\lambda y.(x \ y \ x) $ 
 + 
 +$ \$ and \ \$ true \ \$ false $ 
 + 
 +Care ar rezulta doar în afișarea rezultatului ultimei expresii: 
 + 
 +$ \lambda x.\lambda y.y $ 
 + 
 +**4.1.** ​(//​10p//​) ​Implementați funcția ''​evalCode''​ care primește o strategie de evaluare și o lista de linii de cod, și întoarce o listă cu rezultatele tuturor liniilor de tipul ''​Evaluate''​. 
 + 
 +**4.2.** ​(//​5p//​) ​Implementați funcția ''​parse_code''​ care să parsează o linie de cod.
 <code haskell> <code haskell>
 parse_code :: String -> Code parse_code :: String -> Code
Line 138: Line 171:
 </​note>​ </​note>​
  
-===== Bonus ===== +===== REPL =====
- +
-După cum s-a anunțat la începutul semestrului,​ pentru studenții care au punctaj maxim pe toate 3 temele de pe parcursul semestrului,​ o să se echivaleze examenul din sesiune cu **punctaj maxim**. Pentru a obține acest bonus v-a trebuit realizat și acest task (care altfel nu are punctaj asociat).+
  
-Bonusul este simplu: folosind interpretorul implementat anteriorrealizați un **REPL** ​prin care să fie folosit.+La finalul temeio să puteți să rulați ''​runhaskell main.hs''​ pentru a porni un **REPL**care se folosește de funcțiile și parserul făcute de voi. În acesta puteți ​să evaluați diverse expresii lambda, cum a fost prezentat anterior.
  
-**REPL** poate să fie atât de simplu/​complicat dorițidar trebuie să prezinte cel putin următoarele caracteristici:​ +Pentru a fi mai ușor de utilizatexistă un context ​default în care pornește, cu macro-uri deja definite ​pentru câteva expresii uzuale ​(expresii ​boolenecombinatori).
-  * să citească linii de cod (și să le evalueze, afisând forma normală) +
-  * să păstreze ​un context ​(**i.e.** să pot să fac assign-uri ​și după să folosesc ​macro-urile definite+
-  * să ruleze continuu ​(după introducerea unei expresii ​și returnarea rezultatuluisă aștepte o nouă expresie) +
-  * să detecteze posilele erori de parsare și să nu dea eroare în cazul în care primește o expresie greșită +
-  * **Notă:** nu trebuie neapărat să tratați cazul în care evaluarea efectivă a expresie dă //Stack Overflow//​ +
- +
-<note important>​ +
-Pentru acordarea bonusului, acesta trebuie prezentat asistentului de laborator. +
-</​note>​+
  
 ===== Punctare ===== ===== Punctare =====
Line 170: Line 192:
     * 10p - **4.1.** evaluarea unei secvente de cod     * 10p - **4.1.** evaluarea unei secvente de cod
     * 5p - **4.2.** parsarea de linii de cod     * 5p - **4.2.** parsarea de linii de cod
 +
 +După cum s-a anunțat la începutul semestrului,​ pentru studenții care au punctaj maxim pe toate 3 temele de pe parcursul semestrului,​ o să se echivaleze examenul din sesiune cu punctaj maxim.
  
 <note important>​ <note important>​
Line 181: Line 205:
 ===== Testing ===== ===== Testing =====
  
-Pentru testare puteți ​rula scriptul ''​./​check.sh'',​ care v-a rula un set de teste unitare cu ''​runhaskell ​checker.hs'' ​și v-calcula punctajul total. Puteți rula și direct ''​runhaskell checker.hs''​ dar nu se va calcula punctajul total.+Pentru testare puteți rula un set de teste unitare cu ''​runhaskell ​test.hs''​. Pentru ​testa doar o cerință, puteți da unul din argumentele [lambda | parser | macro | code] pentru a rula cerințele 1, 2, 3 sau 4.
  
 Pentru fiecare test v-a aparea **PASSED** / **FAILED**, și în caz de **FAILED**, diferențele între rezultatul vostru și cel dorit. Pentru fiecare test v-a aparea **PASSED** / **FAILED**, și în caz de **FAILED**, diferențele între rezultatul vostru și cel dorit.
  
 Dacă trec toate testele pentru o cerință, o să apară punctajul (**+X points**), suma tuturor astfel de display-uri este punctajul final. Dacă trec toate testele pentru o cerință, o să apară punctajul (**+X points**), suma tuturor astfel de display-uri este punctajul final.
 +
 +Aveți la dispoziție și un script, ''​check.sh'',​ care vă calculează punctajul final (din 150).
  
 Pentru testarea manuală, puteți să folosiți: '':​l all.hs''​ din **ghci**, care v-a încărca toate fișierele necesare (''​Expr.hs'',​ ''​Lambda.hs'',​ ''​Parser.hs''​) plus un fișier ajutător (''​Tests/​Examples.hs''​),​ în care se află diverse expresii deja declarate (care au fost folosite și in teste). Pentru testarea manuală, puteți să folosiți: '':​l all.hs''​ din **ghci**, care v-a încărca toate fișierele necesare (''​Expr.hs'',​ ''​Lambda.hs'',​ ''​Parser.hs''​) plus un fișier ajutător (''​Tests/​Examples.hs''​),​ în care se află diverse expresii deja declarate (care au fost folosite și in teste).
  
 <note warning> <note warning>
-dacă implementarea unei funcții lipsește (sau apar alte erori) o să apară //„Error: ...”// în loc de **PASSED** / **FAILED**.+Dacă implementarea unei funcții lipsește (sau apar alte erori) o să apară //„Error: ...”// în loc de **PASSED** / **FAILED**
 +</​note>​ 
 + 
 +===== Trimitere ===== 
 + 
 +Temele trebuie submise pe curs.upb.ro,​ în assignment-ul ''​Tema 3''​. 
 + 
 +În arhivă trebuie să se regăsească doar: 
 +  * Expr.hs 
 +  * Lambda.hs 
 +  * Parser.hs 
 +  * main.hs 
 +  * ID.txt - acest fisier va contine o singura linie, formata din ID-ul unic al fiecarui student 
 + 
 +Pentru a verifica format-ul arhivei, aveți în schelet un script în python care face asta: 
 +<​code>​ 
 +python3 archive_validator.py <​archive_name>​ 
 +</​code>​ 
 + 
 +Numele arhivelor trebuie sa fie de forma **<​Nume>​_<​Prenume>​_<​Grupa>​_T3.zip** (daca aveti mai multe prenume sau nume, le puteti separa prin '​-'​). 
 + 
 +<note important>​ 
 +Doar temele care trec de validatorul de arhive o să fie notate.
 </​note>​ </​note>​