Table of Contents

Tipuri de Date Abstracte

Obiective

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:

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:

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.

Constructori de bază

Constructorii de bază reprezintă un set de constructori necesari și suficienți pentru a genera toate valorile posibile ale tipului.

Clasificarea constructorilor de bază (după parametri)

Constructorii unui TDA $t$ se împart în:

Axiome

Axiomele specifică modul în care se comportă operatorii TDA-ului pe toate valorile acestuia.

Reguli pentru scrierea axiomelor:

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.

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:

Exemplu pe tipul Nat

Demonstrăm că add(n, zero) = n, pentru orice număr natural n.

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:

Exemple de gândire

nth (al n-lea element din listă)

Î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)
duplicate (duplicarea fiecărui element al listei)

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)))

Exerciții

Referințe