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/04 18:57] tpruteanu [1. Evaluation] |
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 39: | Line 38: | ||
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> | ||
Când faceți substituția textuală, trebuie să vă asigurați că noile denumiri nu există deja în expresie, pentru asta puteți folosi denumiri imposibile de parsat (care conțin numere de exemplu), dar tot trebuie să verificați că nu ați folosit aceași denumire pentru substituție în trecut. O soluție ar fi să folosiți: $ x_1, x_2, x_3, ..., x_{10}, x_{11}, ... $ și să vericați să nu apară denumirea în corpul sau parametrul funcției. Recomandăm numere doar pentru că sunt mai ușor de generat ca un stream inifinit ca șirurile de caractere. (folosind **[1..]**) | Când faceți substituția textuală, trebuie să vă asigurați că noile denumiri nu există deja în expresie, pentru asta puteți folosi denumiri imposibile de parsat (care conțin numere de exemplu), dar tot trebuie să verificați că nu ați folosit aceași denumire pentru substituție în trecut. O soluție ar fi să folosiți: $ x_1, x_2, x_3, ..., x_{10}, x_{11}, ... $ și să vericați să nu apară denumirea în corpul sau parametrul funcției. Recomandăm numere doar pentru că sunt mai ușor de generat ca un stream inifinit ca șirurile de caractere. (folosind **[1..]**) | ||
- | </not | + | </note> |
- | e> | + | |
===== 2. Parsing ===== | ===== 2. Parsing ===== | ||
Line 68: | 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> | ||
Line 74: | Line 72: | ||
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: | 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> | <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. | + | 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. \\ | ||
Line 89: | Line 87: | ||
*/ | */ | ||
- | **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 **Parser** 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 111: | 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 135: | 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 145: | 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). | + | 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. |
- | Bonusul este simplu: folosind interpretorul implementat anterior, realizați un **REPL** prin care să fie folosit. | + | 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). |
- | + | ||
- | **REPL** poate să fie atât de simplu/complicat doriți, dar trebuie să prezinte cel putin următoarele caracteristici: | + | |
- | * 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 177: | 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 188: | 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> |