Tipuri de Date Abstracte

  • Data publicării: 12.02.2026
  • Data ultimei modificări: 12.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 esențial 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ă: add(zero, zero) = zero (conform axiomei A1).
  • Pas inductiv: 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

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

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

pp/24/laboratoare/tda.txt · Last modified: 2026/02/12 15:20 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