Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pp:26:laboratoare:tda [2026/02/23 10:04] (current)
mihaela.balint created
Line 1: Line 1:
 +====== Tipuri de Date Abstracte ======
 +
 +  * Data publicării:​ 19.02.2026
 +  * Data ultimei modificări:​ 19.02.2026
 +
 +===== Obiective =====
 +Acest laborator introduce conceptul de **Tip de Date Abstract (TDA)**, un instrument fundamental pentru gestionarea complexității în programare și un pas important către paradigma funcțională.
 +
 +Aspecte urmărite:
 +  * **Utilitatea TDA-urilor**:​ Înțelegerea rolului lor în abstractizarea datelor.
 +  * **Specificație**:​ Definirea unui tip prin constructori,​ operatori și axiome.
 +  * **Demonstrații prin inducție structurală**:​ Validarea matematică a proprietăților codului.
 +  * **Axiomele ca bază pentru programarea funcțională**:​ Tranziția de la logică la cod valid.
 +
 +===== Tipuri de Date Abstracte =====
 +Un **TDA (Tip de Date Abstract)** este un model matematic pentru un anumit tip de date, definit nu prin modul în care este stocat în memorie, ci prin **comportamentul** său (ce valori aparțin tipului, ce operații se pot face asupra lor, și ce reguli respectă aceste operații).
 +
 +**De ce este important pentru Programarea Funcțională?​**
 +Ca și în matematică,​ în programarea funcțională nu ne concentrăm pe starea memoriei sau pe pașii de execuție, ci pe transformarea datelor de intrare în rezultate. TDA-urile ne forțează să gândim în termeni de **structură** și **recurență**,​ pregătindu-ne pentru scrierea de cod declarativ, sigur și ușor de testat.
 +
 +Un TDA este definit prin trei elemente:
 +  * **Constructori de bază**: Funcții care creează valorile tipului (ex: cum "​construim"​ un număr sau o listă).
 +  * **Operatori**:​ Funcții care procesează sau interoghează datele (ex: adunare, lungimea listei).
 +  * **Axiome**: Reguli matematice care descriu efectul operatorilor asupra tuturor valorilor tipului.
 +
 +==== Exemple (deocamdată fără axiome) ====
 +^ Tip ^ Constructori de bază ^ Operatori ^ 
 +| **Nat (N)** | zero: -> N \\ succ: N -> N | add: N x N -> N \\ isZero: N -> Bool | 
 +| **List (L)** | null: -> L \\ cons: E x L -> L | head: L -> E \\ tail: L -> L \\ len: L -> N \\ app: L x L -> L | 
 +==== Constructori de tip ====
 +Toți operatorii unui tip $t$ care produc o valoare de tip $t$ se numesc **constructori**.
 +  * **Exemplu (Nat)**: ''​zero'',​ ''​succ''​ și ''​add''​ sunt constructori deoarece toți returnează un număr natural.
 +  * **Contraexemplu**:​ Operatorul ''​isZero''​ (care verifică dacă un număr este zero) **nu** este un constructor pentru ''​Nat'',​ deoarece produce un ''​Bool''​.
 +
 +==== Constructori de bază ====
 +Constructorii de bază reprezintă un set de constructori **necesari și suficienți** pentru a genera toate valorile posibile ale tipului.
 +  * **Exemplu (Nat)**: ''​zero''​ și ''​succ''​ sunt constructori de bază. ​
 +  * **Notă**: ''​add''​ nu este constructor de bază, deoarece orice număr natural poate fi obținut folosind doar ''​zero''​ și ''​succ''​.
 +
 +==== Clasificarea constructorilor de bază (după parametri) ====
 +Constructorii unui TDA $t$ se împart în:
 +  * **Nulari**: Au 0 argumente. Sunt punctele de plecare (ex: ''​zero''​ pentru ''​Nat'',​ ''​null''​ pentru ''​List''​).
 +  * **Externi**:​ Au argumente, dar niciunul nu este de tipul $t$. (În fișa tipurilor ''​Nat''​ și ''​List''​ nu există constructori externi).
 +  * **Interni**:​ Au cel puțin un argument de tip $t$. Aceștia permit recursivitatea structurii (ex: ''​succ''​ pentru ''​Nat'',​ ''​cons''​ pentru ''​List''​).
 +
 +==== Axiome ====
 +Axiomele specifică modul în care se comportă operatorii TDA-ului pe toate valorile acestuia.
 +
 +**Reguli pentru scrierea axiomelor:​**
 +  * **Acoperirea**:​ Axiomele trebuie să acopere toate valorile tipului. De aceea, definim comportamentul operatorilor în raport cu **constructorii de bază**.
 +  * **Neredundanța**:​ Pentru facilitarea demonstrațiilor,​ scriem axiome minimale. ​
 +    * **Exemplu**:​ La operația ''​add(n,​ m)'',​ variem doar primul parametru între ''​zero''​ și ''​succ(n)''​. Nu este necesar să variem și al doilea parametru. În general, când există mai mulți parametri de același tip, scriem axiomele variind doar parametrii pe care vom face recursivitatea.
 +
 +==== Exemple (specificații complete) ====
 +^ Tip ^ Constructori de bază ^ Operatori ^ Axiome ^
 +| **Nat (N)** | zero: -> N \\ succ: N -> N | add: N x N -> N \\ isZero: N -> Bool | (A1) add(zero, m) = m \\ (A2) add(succ(n),​ m) = succ(add(n, m)) \\ \\ (Z1) isZero(zero) = true \\ (Z2) isZero(succ(n)) = false |
 +| **List (L)** | null: -> L \\ cons: E x L -> L | head: L -> E \\ tail: L -> L \\ len: L -> N \\ app: L x L -> L | (H1) head(null) = eroare \\ (H2) head(cons(x,​ L)) = x \\ \\ (T1) tail(null) = eroare \\ (T2) tail(cons(x,​ L)) = L \\ \\ (L1) len(null) = zero \\ (L2) len(cons(x, L)) = succ(len(L)) \\ \\ (P1) app(null, L2) = L2 \\ (P2) app(cons(x, L1), L2) = cons(x, app(L1, L2)) |
 +
 +===== Dimensiunea unei valori și Inducția Structurală =====
 +
 +==== Dimensiunea unei valori ====
 +Dimensiunea unei valori $v$ a unui TDA reprezintă **numărul de constructori de bază** din formula care construiește valoarea.
 +  * **Exemplu**:​ Valoarea ''​succ(succ(zero))''​ are dimensiunea **3** (trei constructori de bază).
 +
 +==== Inducția Structurală ====
 +Inducția structurală este un caz particular al inducției bine formate, și se folosește pentru a demonstra că o proprietate $P(v)$ este adevărată pentru orice valoare $v$ a unui TDA:
 +  * **Cazul de bază**: \\ Demonstrăm $P(v)$ pentru valori de dimensiune minimă (constructori nulari sau externi).
 +  * **Pasul inductiv**: \\ Presupunem că $P$ este adevărată pentru toate valorile de dimensiune $< n$ (ipoteza inductivă) și demonstrăm pentru valori de dimensiune $n$ (obținute prin constructori interni).
 +  ​
 +==== Exemplu pe tipul Nat ====
 +Demonstrăm că ''​add(n,​ zero) = n'',​ pentru orice număr natural ''​n''​.
 +  * **Cazul de bază**: constructorul nular ''​zero''​ \\ add(zero, zero) = zero (conform axiomei ''​A1''​).
 +  * **Pasul inductiv**: constructorul intern ''​succ''​ \\ Ipoteza inductivă: add(n, zero) = n. Demonstrăm proprietatea pentru ''​succ(n)'':​
 +    * add(succ(n),​ zero) = succ(n) <=> (conform ''​A2''​) \\ succ(add(n, zero)) = succ(n) <=> (conform ''​ipotezei inductive''​) \\ succ(n) = succ(n) **q.e.d**
 +
 +
 +
 +===== Gândirea funcțională:​ axiomele sunt cod =====
 +În programarea funcțională,​ scrierea codului este practic scrierea axiomelor. Nu mai gândim în termeni de "ce pași fac", ci "care este definiția cazului"​.
 +
 +Chiar dacă problemele nu sunt formulate ca operatori ai unui TDA, procesul de "​gândire funcțională"​ este următorul:
 +  * **Pe ce variabile este util să fac recursivitate?​** \\ Identificăm variabilele care se "​micșorează"​ structural.
 +  * **Cum mă ajută rezultatul subproblemei?​** \\ Ex: Dacă știu rezultatul pentru ''​tail(L)'',​ cum obțin rezultatul pentru ''​L''?​
 +
 +==== Exemple de gândire ====
 +
 +<code haskell>
 +nth (al n-lea element din listă)
 +</​code>​
 +
 +**Întrebare**:​ Pe ce variabile fac recursivitate?​ \\
 +**Raționament**:​ A identifica al ''​n''​-lea element din ''​L''​ înseamnă a identifica al ''​(n-1)''​-lea element din ''​tail(L)''​. \\
 +**Concluzie**:​ Pe numărul ''​n''​ și pe lista ''​L''​.
 +
 +<code haskell>
 +-- Axiome (cod)
 +nth(null, n)             = eroare
 +nth(cons(x, L), zero)    = x
 +nth(cons(x, L), succ(n)) = nth(L, n)
 +</​code>​
 +
 +
 +<code haskell>
 +duplicate (duplicarea fiecărui element al listei)
 +</​code>​
 +
 +**Notă**: Există un singur parametru (''​L''​),​ deci o singură variabilă pe care pot face recursivitate. \\
 +**Întrebare**:​ Cum transform rezultatul pentru ''​tail(L)''?​ \\
 +**Raționament**:​ Dacă am duplicat toate elementele din ''​tail(L)'',​ trebuie doar să adaug de două ori ''​head(L)''​ la început.
 +
 +<code haskell>
 +-- Axiome (cod)
 +duplicate(null) ​       = null
 +duplicate(cons(x,​ L))  = cons(x, cons(x, duplicate(L)))
 +</​code>​
 +
 +===== Utilitatea TDA-urilor =====
 +Abstractizarea datelor folosind TDA-uri are consecințe importante:
 +  * **Încapsularea detaliilor de implementare**:​ Utilizatorul unui TDA știe $ce$ face o operație (conform axiomelor), nu $cum$ este ea implementată. Putem schimba reprezentarea internă (ex: o listă stocată ca vector sau ca listă înlănțuită) fără a afecta codul care utilizează TDA-ul.
 +  * **Integritatea datelor**: Deoarece datele sunt accesibile doar prin constructori și operatori, se previne modificarea necorespunzătoare a reprezentării interne.
 +  * **Modularitate și reutilizare**:​ Fiecare TDA este o componentă independentă,​ utilizabilă prin intermediul interfeței sale, asemănător cu tipurile predefinite (numere, liste, șiruri de caractere).
 +  * **Validare formală și automatizată**:​ Axiomele sunt atât cod cât și reguli de rescriere, permițând verificarea prin inducție a faptului că programul respectă specificațiile în orice condiții. Pentru că raționamentul se face direct asupra codului, procesul de validare poate fi automatizat.
 +
 +===== Exerciții =====
 +  * Demonstrați prin inducție structurală proprietatea ''​len(app(A,​ B)) = add(len(A), len(B))''​.
 +  * Definiți TDA-ul ''​Arbore binar cu elemente de tip E'',​ cu operatorii ''​size''​ (numărul de elemente din arbore) și ''​flatten''​ (lista rezultată din parcurgerea arborelui în inordine).
 +  * Pentru tipul anterior, demonstrați prin inducție structurală proprietatea ''​size(T) = len(flatten(T))''​. Când demonstrația se blochează, avansați demonstrând o proprietate adițională cât mai simplă.
 +  * Scrieți axiome (cod) pentru următoarele operații:
 +    * **Rotația** unei liste cu ''​n''​ poziții către stânga (ex: $[a, b, c, d, e, f, g]$ rotit cu două poziții devine $[c, d, e, f, g, a, b]$).
 +    * **Ștergerea** elementului de la poziția ''​n''​ din listă (ex: ștergerea celui de-al treilea element din $[a, b, c, d, e, f, g]$ produce $[a, b, d, e, f, g]$).
 +    * **Simetria structurală**:​ Verificați dacă doi arbori binari sunt simetrici (ex: un arbore cu structura ''​node(empty,​ x, node(empty, y, empty))''​ este simetric cu unul de forma ''​node(node(empty,​ z, empty), w, empty)''​).
 +
 +
 +===== Referințe =====
 +  * [[https://​www.ic.unicamp.br/​~meidanis/​courses/​mc336/​problemas-lisp/​L-99_Ninety-Nine_Lisp_Problems.html|Probleme de programare funcțională]]
  
pp/26/laboratoare/tda.txt · Last modified: 2026/02/23 10:04 by mihaela.balint
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0