User Tools

Site Tools


laboratoare:04-tipuri-de-date-abstracte

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
laboratoare:04-tipuri-de-date-abstracte [2015/03/17 15:07]
lucian.mogosanu [TDA-uri monomorfice: Liste monomorfice, inregistrari]
laboratoare:04-tipuri-de-date-abstracte [2016/03/24 14:54] (current)
mihai.dumitru2201 Correction
Line 1: Line 1:
-===== Laborator 4 - Tipuri de date abstracte =====+=====Tipuri de date abstracte ​======
  
-!! In acest laborator, vom specifica explicit tipurile functiilor implementate !!+Responsabili ​laborator:  
 +  * [[mihai.dumitru@cti.pub.ro|Dumitru Mihai-Valentin]] 
 +  * [[calin.cruceru@cti.pub.ro|Călin Cruceru]]
  
-==== TDA-uri monomorficeListe monomorficeinregistrari ​==== +Scopul laboratorului:​ 
-  * Implementati ​TDA-ul: "​lista ​de numere ​naturale"​ +  * Recapitularea conceptului de TDA 
-  * Scrieti ​functie ​care converteste ​valori ale TDA-ului vostru ​in liste din Haskell +  * Definirea de TDA-uri în Haskell 
-  * Implementati ​TDA-ul "​optiune",​ care codifica ​valori ​optionale ​de tipul Integere.g. fie ''​None'',​ fie o valoare ​intreaga +  * Familiarizarea cu conceptul de TDA-uri polimorfice 
-  Scrieti ​un TDA care codifica ​un tuplu (inregistrare) format din urmatoarele campuri:+  * Introducerea tipului de date ''​Maybe''​ 
 + 
 + 
 +===== Tipuri de date abstracte (TDA) ===== 
 + 
 +**Tipurile de date abstracte** sunt modele pentru a defini tipuri de date în funcție de comportamentul acestora (valori posibile, axiome, operații), contrastând cu //​structurile de date//, definite din punct de vedere al implementării. 
 + 
 +Un TDA familiar este **list**. În primul laborator am lucrat cu liste de întregi, definind o listă ca fiind fie lista vidă, fie un întreg introdus în altă listă. Deasemena am definit o mulțime de operații posibile pe listă: ''​isEmpty'',​ ''​head'',​ ''​tail'',​ ''​add'',​ ''​get'',​ ''​ins'',​ ''​show''​.\\ 
 +Pentru a lucra cu liste într-un limbaj ca Java, am scris două //​implementări//​ distincte, și anume ''​LinkedList''​ și ''​ArrayList''​. Ambele implementări respectă specificațiile din primul paragraf. 
 + 
 +<note important>​ 
 +Unii autori includ în descrierea TDA-urilor și complexități. Din acest punct de vedere, implementările  
 +''​LinkedList''​ și ''​ArrayList''​ diferă. 
 +</​note>​ 
 + 
 + 
 +==== Constructori ==== 
 + 
 +Valorile posibile ale unui TDA sunt date de constructorii acestuia.\\ 
 +Să considerăm un tip simplu de date''​Bool''​care poate avea două valori: ''​False''​ și ''​True''​.\\ 
 +Sintaxa Haskell pentru a defini acest tip de date este următoarea:​ 
 + 
 +<code haskell>​ 
 +data Bool False | True 
 +</​code>​ 
 + 
 +<note important>​ 
 +Atât numele tipului de date cât și numele constructorilor trebuie scris cu literă mare. 
 +</​note>​ 
 + 
 +''​False''​ și ''​True''​ nu sunt parametrizate și pot fi privite ca valori. Se mai numesc și //​constructori nulari//​.\\ 
 + 
 +Să considerăm un alt TDA care descrie un arbore binar cu valori întregi. Arborele poate fi gol sau un nod cu doi copii. 
 + 
 +<code haskell>​ 
 +data Tree Nil | Node Int Tree Tree 
 +</​code>​ 
 + 
 +''​Node''​ este un constructor care primește un argument de tipul ''​Int''​ și două argumente de tip ''​Tree'',​ pentru a creea o valoare de tip ''​Tree''​. Putem să ne gândim la el ca la o funcție: 
 + 
 +<​code>​ 
 +Prelude> :t Node 
 +Node :: Int -> Tree -> Tree -> Tree 
 +</​code>​ 
 + 
 +Constructorii sunt foarte importanți în //pattern matching//. Ca exemplu, vom scrie o funcție care însumează toate valorile dintr-un arbore: 
 + 
 +<code haskell>​ 
 +treeSum :: Tree -> Int 
 +treeSum Nil 
 +treeSum (Node v l r) v + (treeSum l) + (treeSum r) 
 +</​code>​ 
 + 
 +Observăm prezența a două pattern-uri:​ unul pentru arborele vid, celălalt pentru un nod, într-un fel foarte asemănător cu modul în care lucram cu liste, făcând pattern matching pe ''​[]''​ și ''​(x:​xs)''​. 
 + 
 +<note tip> 
 +Observați că ghci nu știe cum să afișeze arborele nostru: 
 + 
 +<​code>​ 
 +Prelude> Node 1 Nil Nil 
 + 
 +<​interactive>:​38:​1:​ 
 +    No instance for (Show Tree) arising from a use of `print'​ 
 +    In a stmt of an interactive GHCi command: print it 
 +</​code>​ 
 + 
 +Pentru a vă fi mai ușor să vă verificați,​ puteți obține o reprezentare a tipurilor definite de voi astfel: 
 + 
 +<code haskell>​ 
 +data Tree = Nil | Node Int Tree Tree deriving (Show) 
 +</​code>​ 
 + 
 +Pe scurt, acest lucru înrolează ​TDA-ul ​vostru în clasa ''​Show'',​ oferind o implementare implicită a funcției ''​show''​ (apelată ​de ghci). Mai multe detalii în laboratorul următor. 
 + 
 +<​code>​ 
 +Prelude> Node 1 Nil Nil 
 +Node 1 Nil Nil 
 +</​code>​ 
 +</​note>​ 
 + 
 +==== Exerciții TDA ==== 
 + 
 +1. Implementați TDA-ul pentru propria voastră listă care poate conține doar numere ​întregi. 
 + 
 +2. Scrieți ​funcție ​care convertește ​valori ale TDA-ului vostru ​în liste din Haskell. 
 + 
 +<note tip> 
 +Dacă tipul vostru de listă se numește ''​List'',​ atunci tipul funcției va fi:  
 + 
 +<code haskell>​ 
 +convertList :: List -> [Int] 
 +</​code>​ 
 + 
 +</​note>​ 
 + 
 +3. Implementați ​TDA-ul "​optiune",​ care codifică ​valori ​opționale ​de tip Integer ​(e.g. fie ''​None'',​ fie o valoare ​întreagă). 
 + 
 +<​note>​ 
 +Pentru a ilustra importanța acestui TDA, considerăm următoare situație: vrem să implementăm o funcție care returnează valoarea păstrată în rădăcina unui arbore dat. 
 + 
 +<code haskell>​ 
 +-- presupunem ca Optiune are constructorii "​None"​ si "Value Int" 
 +rootValue :: Tree -> Optiune 
 +rootValue Nil = None 
 +rootValue (Node v _ _) = Value v 
 +</​code>​ 
 + 
 +Un arbore vid nu are nicio valoare în rădăcină,​ iar pentru orice alt arbore, valoarea este cea din nodul primit. Fără TDA-ul ''​Optiune''​ nu am putea avea pattern-uri exhaustive, fără să folosim convenții de valori speciale (e.g. 0, MAX_INT) pentru valoarea din rădăcină. 
 + 
 +Putem scrie o funcție care ia o ''​Optiune''​ și returnează un șir pentru printare. Combinăm apoi cele două funcții: 
 + 
 +<code haskell>​ 
 +pretty :: Optiune -> String 
 +pretty None = "No value"​ 
 +pretty (Value v) = "Value is " ++ show v 
 + 
 +printRootValue :: Tree -> String 
 +printRootValue = pretty . rootValue 
 +</​code>​ 
 + 
 +Încărcăm modulele în ghci și voilà: 
 +<​code>​ 
 +*Main> printRootValue (Node 1 (Node 1 Nil (Node 2 Nil Nil)) (Node 3 (Node 4 (Node 5 Nil Nil) Nil) (Node 6 Nil Nil))) 
 +"Value is 1" 
 +*Main> printRootValue Nil 
 +"No value"​ 
 +</​code>​ 
 +</​note>​ 
 + 
 +<note tip> 
 +Evident, Haskell pune la dispoziție un tip de date similar, numit ''​Maybe''​. Spre deosebire de tipul nostru, ''​Maybe''​ funcționează cu orice tip de date, nu doar întregi, fiind un tip //​polimorfic//​ (mai multe detalii [[laboratoare:​04-tipuri-de-date-abstracte#​tda-uri_polimorfice|aici]]). 
 + 
 +<code haskell>​ 
 +data Maybe a = Nothing | Just a 
 +</​code>​ 
 +</​note>​ 
 + 
 +==== Înregistrări ==== 
 + 
 +Vrem să definim un TDA pentru a descrie un student, folosind următoarele câmpuri: nume, prenume, an de studiu, medie. 
 + 
 +<code haskell>​ 
 +-- tipul de date si constructorul au acelasi nume, ceea ce este ok 
 +-- pentru ca reprezinta concepte diferite. 
 +data Student = Student String String Int Float 
 +</​code>​ 
 + 
 +Observăm că semnficația câmpurilor nu este evidentă din definiție. Vrem să extragem valori dintr-un Student; definim funcțiile:​ 
 + 
 +<code haskell>​ 
 +nume :: Student -> String 
 +nume (Student n _ _ _) = n 
 + 
 +prenume :: Student -> String 
 +prenume (Student _ p _ _) = p 
 + 
 +an :: Student -> Int 
 +an (Student _ _ a _) = a 
 + 
 +medie :: Student -> Float 
 +medie (Student _ _ _ m) = m 
 +</​code>​ 
 + 
 +Pentru a face descrierea tipului de date mai clară și a evita să scriem manual acele funcții, putem folosi sintaxa: 
 + 
 +<code haskell>​ 
 +data Student = Student { nume :: String 
 +                       , prenume :: String 
 +                       , an :: Int 
 +                       , medie :: Float 
 +                       } 
 +</​code>​ 
 +                       
 +                     
 +4. a) Scrieți ​un TDA care codifică ​un tuplu (înregistrare) format din următoarele câmpuri:
      * Nume (codificat ca String)      * Nume (codificat ca String)
-     ​* ​Varsta ​(codificat ca Integer)+     ​* ​Vârstă ​(codificat ca Integer)
      * Prieteni (codificat ca o lista de String-uri)      * Prieteni (codificat ca o lista de String-uri)
-     ​* ​Nota PP (codificat ca Integer). Acest camp este optional+     ​* ​Notă PP (codificat ca Integer). Acest câmp este opțional
-  + 
-  * Scrieti ​functie ​care primeste ​lista de inregistrari,​ si le intoarce ​pe acelea pentru care varsta ​este mai mare ca 20 +b) Scrieți ​funcție ​care primește ​listă ​de înregistrări și le întoarce ​pe acelea pentru care vârsta ​este mai mare ca 20. 
-  * Scrieti ​functie ​care primeste ​lista de inregistrari,​ si le intoarce ​pe acelea care contin ​cel putin un prieten cu numele "​Matei"​ + 
-  * Scrieti ​functie ​care primeste ​lista de inregistrari,​ si le intoarce ​pe acelea care contin campul ​"Nota PP" +c) Scrieți ​funcție ​care primește ​listă ​de înregistrări și le întoarce ​pe acelea care conțin ​cel puțin ​un prieten cu numele "​Matei"​. 
-  * Scrieti ​functie ​care primeste ​lista de nume, o lista de varste, o lista ce contine ​liste de prieteni, ​si intoarce ​o lista de inregistrari corespunzatoare+ 
 +d) Scrieți ​funcție ​care primește ​listă ​de înregistrări și le întoarce ​pe acelea care conțin câmpul ​"Notă PP". 
 + 
 +e) Scrieți ​funcție ​care primește ​listă ​de nume, o listă ​de vârste, o listă ​ce conține ​liste de prieteni, ​și întoarce ​listă de înregistrări corespunzătoare. 
 + 
 +===== TDA-uri polimorfice ===== 
 + 
 +Până acum, am definit TDA-uri care lucrează doar cu întregi. Dorim să scăpăm de această limitare și să definim TDA-uri care pot lucra cu orice alt tip de date. De exemplu, are sens să considerăm capul unei liste care conține elemente de tip ''​a'',​ ca fiind un element de tip ''​a'',​ indiferent de cum arată acest tip.\\ 
 + 
 +Într-adevăr,​ aceasta este definiția funcției ''​head''​ existentă în Haskell: 
 + 
 +<​code>​ 
 +Prelude> :t head 
 +head :: [a] -> a 
 +</​code>​ 
 + 
 +''​a''​ se numește **variabilă de tip** și poate să reprezinte orice tip de date (evident, cu condiția ca toate ''​a''​-urile care apar să se refere la același tip). 
 + 
 +<​note>​ 
 +Să ne uităm la tipul altei funcții cunoscute:​ 
 + 
 +<​code>​ 
 +Prelude> :t map 
 +map :: (a -> b) -> [a] -> [b] 
 +</​code>​ 
 + 
 +''​map''​ primește ca argument o funcție - care primește un element de tip ''​a''​ și întoarce un element de tip ''​b''​ - și o listă de elemente de tip ''​a''​ și întoarce o listă de elemente de tip ''​b''​. De precizat că ''​a''​ și ''​b''​ pot fi distincte dar nu este necesar. 
 +</​note>​ 
 + 
 +Listele din Haskell sunt **polimorfice**. Ele pot conține date de orice tip. O instanță a listelor are însă un tip concret: 
 + 
 +<​code>​ 
 +Prelude> :t ['​a',​ '​b',​ '​c'​] 
 +['​a',​ '​b',​ '​c'​] :: [Char] 
 +</​code>​ 
 + 
 +==== Constante polimorfice ==== 
 + 
 +Care este tipul listei vide? 
 + 
 +<​code>​ 
 +Prelude> :t [] 
 +[] :: [a] 
 +</​code>​ 
 + 
 +Observăm că tipul listei vide este general. Asta înseamnă că lista vidă poate fi tratată ca o listă ​de orice tip. 
 + 
 +<​code>​ 
 +Prelude> [1,2,3] ++ "​123"​ -- eroare, tipuri diferite 
 +Prelude> [1,2,3] ++ [] 
 +[1,2,3] 
 +Prelude> "​123"​ ++ [] 
 +"​123"​ 
 +</​code>​ 
 + 
 +Spunem că ''​[]''​ este o **constantă polimorfică**. 
 + 
 +<​note>​ 
 +O altă constantă polimorfică este, de exemplu ''​1''​. De aceea putem scrie: 
 + 
 +<​code>​ 
 +Prelude> 1 + 2 
 +
 +Prelude> 1 + 2.71 
 +3.71 
 +</​code>​ 
 + 
 +Dacă forțăm 1 să fie întreg, vom primi o eroare la a doua adunare: 
 +<​code>​ 
 +Prelude> (1 :: Int) + 2.71 
 + 
 +<​interactive>:​60:​14:​ 
 +    No instance for (Fractional Int) arising from the literal `2.71'​ 
 +    In the second argument of `(+)', namely `2.71'​ 
 +    In the expression: (1 :: Int) + 2.71 
 +    In an equation for `it': it = (1 :: Int) + 2.71 
 +</​code>​ 
 + 
 +''​1''​ are însă o constrângere despre care vom discuta în laboratorul următor. 
 +</​note>​ 
 + 
 + 
 +Pentru a implementa în Haskell un TDA polimorfic, folosim sintaxa: 
 + 
 +<code haskell>​ 
 +data List a = Empty | Cons a (List a) 
 +</​code>​ 
 + 
 +<​note>​ 
 +Ce este ''​List''?​\\ 
 + 
 +''​List''​ este un **constructor de tip**, nu un tip de date. Constructorii de tip primesc ca parametrii //tipuri// și întorc un tip (sau un alt constructor de tip dacă nu primesc suficienți parametrii).\\ 
 +Asta înseamnă că, ''​List''​ nu este un //tip//, dar ''​List Int'',​ ''​List Char''​ etc. sunt tipuri. 
 +</​note>​ 
 + 
 +==== Exerciții TDA-uri polimorfice ==== 
 + 
 +<note important>​ 
 +Încercați să vă obișnuiți să scrieți explicit tipurile tuturor funcțiilor definite de voi în continuare. 
 +</​note>​ 
 + 
 +5. Pornind de la TDA-ul polimorfic ''​List a''​ definit mai sus: 
 + 
 +a) Reimplementați o funcție ce convertește ''​Lista a''​ în liste (polimorfice) din Haskell.\\ 
 +b) Implementați ''​foldl'',​ ''​foldr''​ și ''​map''​ pentru TDA-ul creat. 
 + 
 +\\ 
 +6. a) Implementați TDA-ul pentru arbori polimorfici.\\ 
 +b) Implementați o funcție ''​tmap''​ care este echivalentul lui ''​map''​ pe arbori.\\ 
 +c) Implementați o funcție ''​tzipWith''​ care este echivalentul lui ''​zipWith''​ pe arbori.\\ 
 +d) Implementați o funcție ce determină suma tuturor elementelor dintr-un arbore.\\ 
 +e) Generalizați funcția anterioară,​ pentru a codifica o operație asociativă pe elementele din arbore.\\ 
 +f) Implementați ''​foldT''​. Câte implementări posibile există?​\\ 
 +g) Implementați ''​tmap''​ pe baza ''​foldT''​.\\ 
 + 
 +\\ 
 +7. a) Implementați TDA-ul ''​Pereche'',​ care poate conține două valori de orice tip.\\ 
 +b) Implementați o funcție care primește două liste (reprezentate folosind TDA-ul anterior) și construiește o listă de perechi. 
 + 
 +===== Referințe =====
  
-==== TDA-uri polimorficeListe, Arbori, Perechi ==== +  * [[https://​wiki.haskell.org/​Abstract_data_type|ADT ​Haskell wiki]] 
-=== Liste polimorfice === +  * [[https://​wiki.haskell.org/​Maybe|Maybe ​- Haskell ​wiki]]
-  * Implementati TDA-ul "Lista polimorfica"​ +
-  * Re-implementati o functie ce converteste Liste definite anterior, in liste (polimorfice) din Haskell +
-  * Implementati ''​foldl'',​ ''​foldr''​ si ''​map''​ pentru TDA-ul creat+
  
-=== Arbori polimorfici ​=== +===== Soluții laborator =====
-  * Implementati TDA-ul "​Arbore polimorfic"​ +
-  * Implementati o functie ''​tmap''​ care este echivalentul lui ''​map''​ pe arbori +
-  * Implementati o functie ''​tzipWith''​ care este echivalentul lui ''​zipWith''​ pe arbori +
-  * Implementati ''​tfoldl''​ si ''​tfoldr''​+
  
-=== Perechi === +  ​[[https://​github.com/​Programming-Paradigms/​Labs/​archive/​master.zip|Rezolvări]]
-  ​Implementati TDA-ul "​Pereche"​Observatie: O pereche poate contine valori de orice tip +
-  * Implementati o functie care primeste doua liste (reprezentate folosind TDA-ul anterior) si construieste o lista de perechi+
laboratoare/04-tipuri-de-date-abstracte.1426597642.txt.gz · Last modified: 2015/03/17 15:07 by lucian.mogosanu