This is an old revision of the document!
Acest laborator introduce conceptul de Tip de Date Abstract (TDA), un instrument fundamental pentru gestionarea complexității în programare și un pas esențial către paradigma funcțională.
Aspecte urmărite:
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:
| 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 |
Toți operatorii unui tip $t$ care produc o valoare de tip $t$ se numesc constructori.
zero, succ și add sunt constructori deoarece toți returnează un număr natural.isZero (care verifică dacă un număr este zero) nu este un constructor pentru Nat, deoarece produce un Bool.Constructorii de bază reprezintă un set de constructori necesari și suficienți pentru a genera toate valorile posibile ale tipului.
zero și succ sunt constructori de bază. add nu este constructor de bază, deoarece orice număr natural poate fi obținut folosind doar zero și succ.Constructorii unui TDA $t$ se împart în:
zero pentru Nat, null pentru List).Nat și List nu există constructori externi).succ pentru Nat, cons pentru List).Axiomele specifică modul în care se comportă operatorii TDA-ului pe toate valorile acestuia.
Reguli pentru scrierea axiomelor:
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.| 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 $v$ a unui TDA reprezintă numărul de constructori de bază din formula care construiește valoarea.
succ(succ(zero)) are dimensiunea 3 (trei constructori de bază).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:
Demonstrăm că add(n, zero) = n, pentru orice număr natural n.
A1).succ(n):A2)ipotezei inductive)Î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:
tail(L), cum obțin rezultatul pentru L?
Î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$.
Axiome (cod) ------------ nth(null, n) = eroare nth(cons(x, L), zero) = x nth(cons(x, L), succ(n)) = nth(L, n)
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.
Axiome (cod) ------------ duplicate(null) = null duplicate(cons(x, L)) = cons(x, cons(x, duplicate(L)))
len(app(A, B)) = add(len(A), len(B)).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).size(T) = len(flatten(T)).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]$).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]$).node(empty, x, node(empty, y, empty)) este simetric cu unul de forma node(node(empty, z, empty), w, empty)).