Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
pp:l05 [2019/03/24 15:43] dmihai |
pp:l05 [2020/02/05 15:50] (current) dmihai [Exerciții] |
||
---|---|---|---|
Line 81: | Line 81: | ||
O **clasă de tipuri** (**typeclass**) este o colecție de funcții care specifică un comportament. Un tip de date poate fi //înrolat// într-o clasă, furnizând implementări ale funcțiilor, particularizate pentru acest tip. | O **clasă de tipuri** (**typeclass**) este o colecție de funcții care specifică un comportament. Un tip de date poate fi //înrolat// într-o clasă, furnizând implementări ale funcțiilor, particularizate pentru acest tip. | ||
- | <note warning> | + | <note important> |
În ciuda numelui, clasele Haskell nu corespund claselor din limbaje de programare orientate pe obiecte (e.g. Java, C%%+%%%%+%%). Clasele Haskell //nu conțin date// și //nu pot fi instanțiate//. O comparație mai pertinentă este cu interfețele din Java. | În ciuda numelui, clasele Haskell nu corespund claselor din limbaje de programare orientate pe obiecte (e.g. Java, C%%+%%%%+%%). Clasele Haskell //nu conțin date// și //nu pot fi instanțiate//. O comparație mai pertinentă este cu interfețele din Java. | ||
</note> | </note> | ||
Line 225: | Line 225: | ||
Empty == Empty = True | Empty == Empty = True | ||
(Cons a as) == (Cons b bs) = (a == b) && (as == bs) | (Cons a as) == (Cons b bs) = (a == b) && (as == bs) | ||
+ | _ == _ = False | ||
</code> | </code> | ||
- | Primul rând exprimă ideea că putem compara două liste //de același tip// (''List a''), doar dacă //elementele lor sunt comparabile// (''(Eq a) =%%>%%''). | + | Primul rând exprimă ideea că putem egala două liste //de același tip// (''List a''), doar dacă //elementele lor sunt egalabile// (''(Eq a) =%%>%%''). |
Observați că, în expresia celui de-al doilea caz, funcția ''=='' apare de două ori: o dată pentru a compara elemente, o dată pentru a compara liste. Acesta este un exemplu de **polimorfism ad hoc**. Cele două apeluri de ''=='' pot avea implementări diferite (al doilea este mereu un apel recursiv; primul poate fi, de exemplu, comparație între doi întregi, în cazul ''List Int''). | Observați că, în expresia celui de-al doilea caz, funcția ''=='' apare de două ori: o dată pentru a compara elemente, o dată pentru a compara liste. Acesta este un exemplu de **polimorfism ad hoc**. Cele două apeluri de ''=='' pot avea implementări diferite (al doilea este mereu un apel recursiv; primul poate fi, de exemplu, comparație între doi întregi, în cazul ''List Int''). | ||
Line 282: | Line 283: | ||
<note> | <note> | ||
- | Respectarea strictă a tuturor legilor unei clase are excepții. De exemplu, pentru operațiile în virgulă mobilă, o valoare specială [[https://en.wikipedia.org/wiki/NaN|NaN (Not a Number)]] modelează rezultatul anumitor expresii cu rezultat nedefinit. Două valori ''NaN'' nu sunt considerate egale; prin urmare, pentru tipuri ca ''Float'' și ''Double'', operația ''=='' nu respectă proprietatea de reflexivitate: | + | Respectarea strictă a tuturor legilor unei clase are excepții. De exemplu, pentru operațiile în virgulă mobilă, o valoare specială [[https://en.wikipedia.org/wiki/NaN|NaN (Not a Number)]] modelează rezultatul anumitor expresii cu rezultat nedefinit. Două valori ''NaN'' nu sunt considerate egale; prin urmare, pentru tipuri ca ''Float'' și ''Double'', operația ''=='' nu respectă proprietatea de reflexivitate pe întreg domeniul: |
<code> | <code> | ||
Line 293: | Line 294: | ||
===== Exerciții ===== | ===== Exerciții ===== | ||
- | 1. În [[pp:l04|laboratorul anterior]], ați definit un tip pentru a modela numerele naturale extinse cu un punct la infinit, precum și niște operații pe acestea. Dorim să facem implementarea elegantă, pentru a putea folosi operatori deja existenți (e.g. ''=='' pentru comparare) și pentru a putea folosi alte funcții existente care impun constrângeri de tip (e.g. ''Data.List.sort :: Ord a =%%>%% [a] -%%>%% [a]''). Astfel, ne dorim să înrolăm tipul de date în următoarele clase: | + | 1. Înrolați tipul ''List a'' în clasa ''Show'', astfel încât șirul rezultat să fie identic cu cel pentru listele Haskell: |
+ | |||
+ | <code> | ||
+ | *Main> Cons 1 (Cons 2 (Cons 3 Empty)) | ||
+ | [1,2,3] | ||
+ | </code> | ||
+ | |||
+ | 2. În [[pp:l04|laboratorul anterior]], ați definit operații (''foldrT'', ''mapT'' etc.) pentru lucrul cu arbori binari polimorfici. Am vrea să înrolăm acest tip la clasele corespunzătoare. | ||
- | * Show (astfel încât să afișăm numerele fără a fi precedate de vreun nume de constructor, iar infinitul ca "Inf") | + | a. înrolați ''BTree a'' în ''Eq'' |
- | * Eq | + | |
- | * Ord | + | b. înrolați ''BTree'' în ''Foldable'' și definiți ''foldr'' cu aceeași definiție ca ''foldrT'' din laboratorul trecut; veți avea apoi acces la funcții ca ''foldl'', ''sum'', ''minimum'' ('':info Foldable'' în ''ghci'' pentru a le vedea pe toate) |
- | * Num | + | |
+ | c. înrolați ''BTree'' în ''Functor'' și definiți ''fmap'' cu aceeași definiție ca ''mapT'' din laboratorul trecut (mai multe informații [[https://wiki.haskell.org/Functor|aici]]) | ||
+ | |||
+ | <note important> | ||
+ | Atenție la tipurile ''foldr'' și ''fmap'': | ||
+ | |||
+ | <code> | ||
+ | Prelude> :t foldr | ||
+ | foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b | ||
+ | Prelude> :t fmap | ||
+ | fmap :: Functor f => (a -> b) -> f a -> f b | ||
+ | </code> | ||
+ | |||
+ | Observați că constrângerile sunt puse pe //constructorul de tip//, deci va trebui înrolat ''BTree'', nu ''BTree a''. | ||
+ | </note> | ||
+ | |||
+ | 3. Tot în [[pp:l04|laboratorul anterior]], ați definit un tip pentru a modela numerele naturale extinse cu un punct la infinit, precum și niște operații pe acestea. Dorim să facem implementarea elegantă, pentru a putea folosi operatori deja existenți (e.g. ''=='' pentru comparare) și pentru a putea folosi alte funcții existente care impun constrângeri de tip (e.g. ''Data.List.sort :: Ord a =%%>%% [a] -%%>%% [a]''). Înrolați tipul ''Extended'' în clasele: | ||
+ | |||
+ | a. ''Show'' (a.î. să afișăm numerele fără vreun nume de constructor, iar infinitul ca "Inf") | ||
+ | |||
+ | b. ''Eq'' | ||
+ | |||
+ | c. ''Ord'' | ||
+ | |||
+ | d. ''Num'' | ||
+ | |||
+ | 4. Definiți un tip de date polimorfic care să modeleze următoarea gramatică (în care o valoare poate avea orice tip): | ||
- | 2. Definiți un tip de date asociat următoarei gramatici: | ||
<code> | <code> | ||
<expr> ::= <value> | <variable> | <expr> + <expr> | <expr> * <expr> | <expr> ::= <value> | <variable> | <expr> + <expr> | <expr> * <expr> | ||
</code> | </code> | ||
- | unde o valoare poate avea orice tip. | ||
- | 3. Considerăm următorul constructor de tip: | + | 5. Amintiți-vă de tabelele asociative din laboratorul trecut. Vom defini un "dicționar" ca fiind o tabelă asociativă cu șiruri de caractere drept chei: |
<code haskell> | <code haskell> | ||
type Dictionary a = [(String, a)] | type Dictionary a = [(String, a)] | ||
</code> | </code> | ||
- | care modeleaza //dicționare// - mapări de tip "nume-variabilă"-"valoare polimorfica" | ||
- | Definiți funcția: | + | Ne mai interesează și funcția care primește o cheie și o tabelă asociativă și întoarce valoarea asociată cheii, împachetată într-un ''Maybe'': |
- | <code>valueof :: Dictionary a -> String -> Maybe a</code> | + | |
- | care intoarce valoarea asociata unui nume-variabilă, dintr-un dicționar | + | <code haskell> |
+ | getValue :: String -> Dictionary a -> Maybe a | ||
+ | </code> | ||
+ | |||
+ | Fie următoarea clasă: | ||
- | 4. Definiți următoarea clasă: | ||
<code haskell> | <code haskell> | ||
class Eval t a where | class Eval t a where | ||
Line 325: | Line 360: | ||
<note> | <note> | ||
- | Acest tip de clasă reprezintă o extensie a limbajului, **Multi-parameter type-class**. Cel mai probabil este nevoie de următoarele directive pentru a o defini și pentru a înrola tipuri la ea: | + | Acest tip de clasă reprezintă o extensie a limbajului, **Multi-parameter type-class**. Cel mai probabil este nevoie de următoarele directive //la începutul fișierului// pentru a o defini și pentru a înrola tipuri la ea (directivele se găsesc deja în scheletul de laborator): |
<code haskell> | <code haskell> | ||
Line 332: | Line 367: | ||
class Eval t a where | class Eval t a where | ||
- | eval :: Dictionary a -> t a -> Result a | + | eval :: Dictionary a -> t a -> Maybe a |
</code> | </code> | ||
Line 338: | Line 373: | ||
</note> | </note> | ||
- | 5. Înrolați ''Expr'' și ''Integer'' în clasa ''Eval''. Care este semnificația evaluării? | + | a. Înrolați ''Expr'' și ''Integer'' în clasa ''Eval''. Care este semnificația evaluării?\\ |
- | + | b. înrolați ''Expr'' și ''Extended'' în clasa ''Eval''.\\ | |
- | 6. Înrolați ''Expr'' și ''FIFO a'' în clasa ''Eval''. Semnificația înmulțirii este //concatenarea// a două FIFO. | + | c. Ce observați? Cum ați putea simplifica? |
- | + | ||
- | 1. Ați definit, în laboratorul anterior, tipurile polimorfice ''List a'' și ''Tree a''. Pentru le putea reprezenta, ați folosit implementarea implicită a funcției ''show'', oferită de ''deriving (Show)''. Aceasta nu era însă o reprezentare citibilă. | + | |
- | + | ||
- | Ne dorim să reprezentăm tipul listă, la fel ca cel existent în Haskell: | + | |
- | + | ||
- | <code> | + | |
- | Prelude> show (Cons 1 (Cons 2 (Cons 3 Nil))) | + | |
- | [1,2,3] | + | |
- | </code> | + | |
- | + | ||
- | (Pentru arbori există multe reprezentări posibile, puteți alege orice reprezentare preferați). | + | |
- | + | ||
- | 2. Înrolați aceleași tipuri în clasa ''Eq''. | + | |
- | + | ||
- | 3. Implementați sortarea pentru ''List a'', unde ''a'' e un tip oarecare înrolat în clasa ''Ord''. | + | |
- | + | ||
- | 4. Implementați căutarea binară pentru ''Tree a'', unde ''a'' e un tip oarecare înrolat în clasa ''Ord''. | + | |
- | + | ||
- | 5. Înrolați tipurile de date ''List'' și ''Tree'' în clasa ''Functor''. | + | |
- | ===== Resurse ===== | + | {{:pp:lab5_-_schelet.zip|Lab 5 - Schelet}}\\ |
+ | ===== Recommended Reading ===== | ||
- | * [[https://www.haskell.org/tutorial/classes.html|Type Classes and Overloading - Haskell wiki]] | + | * [[http://learnyouahaskell.com/types-and-typeclasses#typeclasses-101|Learn you a Haskell for Great Good - Chapter 2: Types and Typeclasses#Typeclasess 101]]\\ |
- | * [[https://wiki.haskell.org/Typeclassopedia|Typeclassopedia - Haskell wiki]] | + | * [[http://learnyouahaskell.com/making-our-own-types-and-typeclasses#typeclasses-102|Learn you a Haskell for Great Good - Chapter 8: Making Our Own Types and Typeclasses#Typeclasess 102]]\\ |
- | * [[http://learnyouahaskell.com/types-and-typeclasses#typeclasses-101|Typeclasses 101 - Learnyouahaskell]] | + | * [[http://book.realworldhaskell.org/read/using-typeclasses.html|Real World Haskell - Chapter 6: Using Typeclasses]]\\ |
- | * [[http://learnyouahaskell.com/making-our-own-types-and-typeclasses#typeclasses-102|Typeclasses 102 - Learnyouahaskell]] | + | * [[https://www.haskell.org/tutorial/stdclasses.html|A Gentle Introduction to Haskell - Chapter 8: Standard Haskell Classes]] |