Differences
This shows you the differences between two versions of the page.
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)$. \\ | + | Astfel, expresia 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 progres. Dacă 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 infinita. Sa 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 anterior, realizați un **REPL** prin care să fie folosit. | + | La finalul temei, o 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ți, dar trebuie să prezinte cel putin următoarele caracteristici: | + | Pentru a fi mai ușor de utilizat, există un context default în care pornește, cu macro-uri deja definite pentru câteva expresii uzuale (expresii boolene, combinatori). |
- | * 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 rezultatului, să 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-a 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 a 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> |