Documente Academic
Documente Profesional
Documente Cultură
Fie w ∈ E*, w = e1 e2...en un cuvânt (şir) din E*, unde ei, i∈1..n,
reprezintă o mulţime generică. Notăm ×w produsul cartezian ×w = e1 × e2 ×...× en.
Dacă Ai este o mulţime particulară (cu elemente precizate) asociată simbolului ei din
E, i=1,n, iar w∈ E+, notăm Aw = A1 × A2 ×...× An. Şirul λ are o semnificaţie
specială, fiind folosit în reprezentarea simbolurilor funcţionale nulare.
x ∈ As
hs w hw w
> x'∈ A's y∈ A > y'∈ A'
^ ^
v v
σ ∈ Σ λ ,s x ∈ As > x'∈ A's
hs
Exemplul 3 Fie stk= (A,δ) algebra de signatură ΣS din exemplul 2, iar stk'=
(A',δ') algebra de signatură ΣS, definită după cum se arată mai jos, unde l∈A'stack
şi e∈ A'elm.
A'elm = int
A'stack = int list (liste cu elemente din A'elm, deci liste de întregi)
A'bool = {true, false}
hstack 〈0,v0〉 = []
hstack 〈n,v〉 = [ord v[n]; ord v[n-1]; ... ; ord v[1]]
helm (c) = ord c
hbool(1) = true, iar hbool(0) = false
unde v, v0∈ char vector, n∈int, c∈char, iar ord: char → int este o funcţie de
conversie a caracterelor în întregi. Se poate verifica uşor că h este un morfism de la
(A, δ) la (A', δ'). De exemplu, pentru operatorii newstack şi push diagramele de
mai jos comută.
elm stack
hstack h
<0,v0> >[] c,<n,v> > e, [ e ;...;e n ]
1
^ ^
v v
newstack <n+1,v'> > [e; e 1 ;...;en ]
hstack
v[k] = v'[k], k=1,n e = ord(c)
v'[n+1] = c e k = ord v[n-k+1], k=1,n
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 3
• Suportul A
1) As ⊆ As, s∈S
2) σ ∈ As, pentru σ∈Σ λ,s, s∈S
3) σ(x1, x2,..., xn) ∈ As,
pentru σ ∈ Σw,s, s∈S, w∈S+, x1, x2,..., xn ∈ Aw
• Funcţiile δ
1) δ λ,s(σ) = σ, pentru σ∈Σ λ,s
2) δw,s(σ) (x1, x2,..., xn) = σ(x1,x2,...,xn),
pentru σ ∈ Σw,s, s∈S, w∈S+, x1, x2,..., xn ∈ Aw
După cum se remarcă, algebra liber generată (A, δ) a stivei reprezintă o bază
pentru descrierea abstractă a valorilor de tip stivă sau a valorilor derivate din
prelucrarea unei stive.
id
As > As
gs v v hs
Qs
h (σ)=δ λ ,s (σ)
σ ∈ As s δλ,s(σ) ∈ Qs diagrama (a)
>
^ ^
σ ∈ Σ λ ,s
w hw w
(x , x ,..., x n) ∈ A > hs1 (x1 ),hs2 (x2 ),..., hsn (xn ) ∈ Q
1 2
δ (σ) δw,s(σ)
w,s
v v
σ (x ,x ,..., x n) ∈ A s > δ w,s(σ)( hs1 (x1 ),hs2 (x2 ),..., hsn (xn )) ∈ Qs
1 2 hs
h este un morfism de la (A, δ) la (Q, δ), iar implementarea stivei sub formă
de listă este un caz particular de reprezentare a tipului stivă bazată pe utilizarea
dubleţilor 〈 int, char vector〉.
suport al lui S. Algebra liber generată de signatură Op şi suport A, notată (A, δ)t, se
numeşte algebra iniţială a tipului t.
Corolar 1 Algebra iniţială (A, δ)t este cea mai generală algebră din clasa
algebrelor de signatură ΣS şi suport A = {a∈Atr • Aa} ∪ {At}, pentru orice
particularizare a mulţimii At. Orice asemenea algebră poate fi privită ca un caz
particular al algebrei iniţiale (A, δ)t.
Definiţia 6 Fie (A, δ)t algebra iniţială a unui tip t de obiecte şi E o mulţime de
relaţii de echivalenţă (reflexive, simetrice şi tranzitive) peste A, relaţii numite axiome
ale tipului t, astfel încât valorile simbolice ale (A, δ)t ce fac parte dintr-o clasă de
echivalenţă sub E au aceeaşi semnificaţie din punctul de vedere al tipului t. Algebra
cât (A, δ)t/E este tipul de dată abstractă t.
<signatura Op>
1) valori din As, s∈Atr, aparţinând tipurilor externe lui t, valori care nu conţin
operatori ai lui t. Aceste valori sunt denumite valori externe lui t şi pot fi constante şi
variabile din As, precum şi valori simbolice formate cu constante, variabile şi operatori
caracteristici tipurilor externe desemnate de sorturile Atr.
2) valori simbolice din As, s∈ Atr ∪ {t}, formate cu operatori din Op, valori
externe tipului t, în care pot să apară operatori ai lui t şi variabile cu valori în As.
pop(push(e,stk)) = stk
top(push(e,stk)) = e
empty(newstack) ∧ ¬empty(push(e,stk))
Se observă că orice valoare de tipul stack poate fi descrisă folosind doar doi
operatori: newstack şi push. Orice stivă obţinută folosind pop poate fi reconvertită
conform axiomelor la o stivă formată cu newstack şi push. De exemplu, push(1,
pop(push(2, newstack))) = push(1, newstack). Din acest motiv, newstack şi
push sunt denumiţi constructori de bază, în timp ce pop este un constructor auxiliar.
Valorile construite doar cu constructori de bază sunt numite valori canonice.
Operatorul top este de selecţie, deoarece reîntoarce un element component al unei
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 7
newstack: stack
push: elm × stack → stack
t(<sort1 Atr>, ... , <sortn Atr>)::= <forme canonice ale valorilor lui t>
<signatura Op>
exception emptystack;;
Exception emptystack defined.
let pop = fun newstack -> raise emptystack | (push(e,stk)) -> stk
and top = fun newstack -> raise emptystack | (push(e,stk)) -> e
and empty = fun newstack -> true | _ -> false
;;
pop: 'elm stack -> 'elm stack = <fun>
top: 'elm stack -> 'elm = <fun>
empty: 'elm stack -> bool = <fun>
let pop = fun newstackk -> raise emptystack | (pushh(e,stk)) -> stk
and top = fun newstackk -> raise emptystack | (pushh(e,stk)) -> e
and empty = fun newstackk -> true | _ -> false
Constructorii de bază ai unui tip descriu valorile tipului fie prin istoria devenirii
acestora, fie prin forma lor instantanee. Axiomele prezentării tipului vor reflecta
punctul de vedere adoptat. Notăm:
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 9
• Ext mulţimea operatorilor externi tipului, care produc valori externe tipului, cu
Ext = Sel ∪ Car, unde Sel este mulţimea selectorilor, iar Car mulţimea operatorilor
de caracterizare.
De notat că insset descrie istoria formării unei mulţimi prin inserări succesive
de elemente şi nu asigură unicitatea apariţiei elementelor în reprezentarea simbolică a
mulţimii construite. Totuşi, comportarea corectă a operatorilor delset, cardset şi
inset este garantată prin axiome, care ţin seamă de natura lui insset. Într-adevăr, fie
mulţimea s = insset(a,insset(a,newset)). Atunci, delset(a,s) elimină, conform
axiomei (8), toate apariţiile lui a în s şi produce newset, iar cardset(s) este 1,
ambele rezultate fiind corecte.
set.ml
let rec
newset() = newSet
and insset = fun (e,newSet) -> bodyset(e,newSet)
| (e,bodyset(x,s)) -> if e = x then bodyset(e,s)
else bodyset(x,insset(e,s))
and delset = fun (e,newSet) -> newSet
| (e,bodyset(x,s)) -> if e = x then s
else bodyset(x,delset(e,s))
and inset = fun (e,newSet) -> false
| (e,bodyset(x,s)) -> if e = x then true else inset(e,s)
and cardset = fun newSet -> 0
| (bodyset(_,s)) -> 1 + cardset(s)
and emptyset = fun newSet -> true | _ -> false ;;
set.mli
2 Corectitudinea TDA
Problema corectitudinii se pune atât în momentul construcţiei unui TDA (mai
precis, în faza de sinteză a axiomelor prezentării tipului), cât şi pentru verificarea unui
TDA deja construit.
a) t este complet. Pentru orice valori x,y∈As, s∈ Atr ∪ {t}, egalitatea x=y
poate fi probată pe baza axiomelor E şi a axiomelor tipurilor externe Atr.
Noul TDA, notat setn, este incorect, fiind inconsistent. Într-adevăr, valoarea
v = inset(e,delset(e,s)) din Abool, cu s = insset(e, insset(x, insset(e,
newset))), se poate reduce în mod diferit:
v ⎯ axioma 9 → false
v ⎯ axioma 8' → inset(e, insset(x, insset(e, newset)))
⎯ axioma 6 → inset(e,insset(e,newset))
⎯ axioma 6 → true
În afară de inconsistenţa lui setn, tipurile set şi stack definite mai sus sunt
incomplete. De exemplu, echivalenţa, aparent evidentă,
Exemplul 8 Pentru varianta (b) a prezentării TDA set să verificăm prin inducţie
structurală proprietatea:
P1(s) =def ∀e: elm • ¬ inset(e, delset(e,s))
P2'(bodyset(x,s))=(P2(bodyset(x,s))⇒
P2(delset(e, bodyset(x,s)))) = P2(delset(e, bodyset(x,s)))
[ xi ∈ t • P(xi) ]
________________________________ ∀σ ∈ Opw,t ∩ Conb,
P(σ(...,x1, ...,xi,...,xn,...)) w ∈ S+, t este în w
Fie p o relaţie de ordine definită peste o mulţime X, astfel încât orice şir ...p xn
p...p x2 p x1 este finit. Se spune că relaţia p este bine formată. De exemplu,
relaţia < definită peste mulţimea numerelor naturale este bine formată. Pentru orice
număr natural x1 şirul ...< xn < ...< x1 este evident finit. În schimb, relaţia < pentru
mulţimea int a numerelor întregi nu este bine formată; şirul ...< -10 < ...< -1 < 1
nu este finit.
ipoteză inductivă
|
[ ∀y: X | y pf x • P(y)]
_______________________ pas inducţie
P(x)
_______________
∀x: X • P(x)
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 16
nivel(σ,f) =
• 0, dacă σ nu apare în f
• max {h(arb(f)σ},
dacă f are forma ρ(... σ...), unde arb(f)σ este un subarbore
din arb(f) cu rădăcina σ, iar h(a) este înălţimea arborelui a
• max {v ∈ {vp,v1,v2} • nivel(σ,v)},
dacă f are forma if vp then v1 else v2
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 17
De exemplu,
nivel(delset, delset(e,bodyset(x,r))) = 3
nivel(delset,bodyset(x,delset(e,r)) = 2
nivel(delset,newset) = 0
nivel(delset, if e=x then s else bodyset(x,delset(e,s))) = 2
n
Cona = U Cona , Cona ∩ Cona = ∅ , pentru i ≠ j.
i i j
i = 1
strict monotonă relativ la nivelul lui σa, astfel încât singurii constructori admişi în v sunt
din mulţimea
i − 1
Con b ∪ {σ a} ∪ ( U Cona )
j
j = 1
Mulţimea Cona este convertibilă la Conb dacă fiecare σa ∈ Cona este convertibil
la Conb. Dacă pentru constructorul σa ∈ Cona, convertibil la Conb, axiomele de
convertibilitate nu conţin alţi constructori auxiliari, în afară de σa, se spune ca σa este
direct convertibil la Conb.
n
Ext = U Exti, Exti ∩ Extj = ∅ , pentru i ≠ j
i = 1
strict monotonă relativ la nivelul lui σ, astfel încât singurii operatori admişi în v fac
parte din mulţimea
i − 1
Con b ∪ {σ} ∪ ( U Extj)
j = 1
Mulţimea Ext este reductibilă în raport cu Conb dacă fiecare σ ∈ Ext este
reductibil în raport cu Conb.
na ne
Conb, Cona = U Cona şi Ext = U Exti .
i
i = 1 i = 1
Decidabilitate.
a) Fie v o valoare de tip t, v = σ(... σa(...)...), cu σa∈Cona. Constructorul
σa poate fi eliminat din v. Într-adevăr, termenul σa(...) poate fi substituit în v
conform axiomelor de convertibilitate, obţinând o valoare v', astfel încât
nivel(σa,v) > nivel(σa,v'). Prin inducţie, după nivelul lui σa, se ajunge la o
valoare vm cu nivel(σa,vm)=0, deci, care nu conţine σa. Deoarece această
proprietate este valabilă pentru σa ales arbitrar, rezultă că toţi constructorii auxiliari
pot fi eliminaţi din formule.
Consistenţă.
Deoarece pentru orice σ∈Cona ∪ Ext şi σb∈Conb există o singură axiomă de
convertibilitate sau reductibilitate, iar operaţiile tipurilor externe Atr sunt consistente,
ordinea de reducere a unei valori simbolice v∈At corespunzătoare TDA t este unică,
deci valoarea redusă este unică.
newstack: → stackv
push: elm × stackv → stackv
pop: stackv \ {newstack} → stackv
top: stackv \ {newstack} → elm
empty: stackv → bool
Prezentarea TDA stackv descrie mai curând tipul stivă al cărei corp este un
vector, în loc de stivă, în general. Nivelul de abstractizare are de suferit în favoarea
apropierii specificării în raport cu o posibilă implementare a tipului stackv, o stivă
fiind reprezentată ca o înregistrare cu două câmpuri: indexul de vârf al stivei şi
vectorul cu elementele stivei.
Deşi prezentarea este corectă, totuşi ea este mai puţin clară decât cea iniţială -
pentru TDA stack. Proprietăţile elementare pop(push(e,s)) = s şi top(push(e,s))
= e nu mai rezultă explicit din axiome, ci trebuie demonstrate ca proprietăţi ale stivei.
În general, vom evita astfel de prezentări, cu atât mai mult cu cât precizarea
implementării unui TDA se poate realiza foarte bine la nivel abstract.
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 21
6 Reificarea TDA
Fie ti = 〈Atri,Opi,Ei,Ri〉, i=1,n, o mulţime de TDA folosite ca suport al
n
implementării unui TDA t = 〈Atr,Op,E,R〉. Notăm T = × t i mulţimea punctelor
i = 1
〈x1, x2, ...,xn〉, xi ∈ti, formate cu valori ale tipurilor suport.
Cele două funcţii sunt specificate folosind axiome de abstractizare, pentru Abs,
şi axiome de implementare, pentru Imp. Procesul de implementare a unui tip abstract
pe baza altor tipuri, considerate predefinite în contextul implementării, poartă numele
de rafinare sau reificare. În cele ce urmează, ne limităm doar la implementarea prin
abstractizare.
b) σ = Abs〈x1,x2,...,xn〉, σ∈Opλ,t.
Restricţionarea implementării
Se observă că, în lipsa unor restricţii adecvate, funcţiile Abs şi Imp pun
probleme. Astfel Abs nu este injectivă. Bunăoară, în cazul exemplului 11, conform
axiomelor tipului stack şi axiomelor de abstractizare, pentru orice e: elm avem:
newstack = pop(push(e,Abs〈0,newvec〉)) =
pop(Abs〈1,v〉) = Abs〈0,v〉, cu v = assign(e,1,newvec)
newstack = Abs〈0,newvec〉
şi, în general, pentru orice v: vector(elm), newstack = Abs〈0,v〉. Deci, Abs nu este
injectivă.
Cu alte cuvinte, doi dubleţi 〈i,v1〉 şi 〈j,v2〉 sunt echivalenţi (reprezintă aceeaşi
stivă) dacă i = j şi conţinutul celor doi vectori este acelaşi pentru indici între 1 şi i. O
clasă de echivalenţă din T/Re desemnează o reprezentare dusă de Abs într-o stivă
abstractă, iar reprezentările distincte din T/Re sunt transformate de Abs în stive
diferite.
unde, TInv este submulţimea reprezentărilor valorilor lui t, mulţime filtrată prin
invariantul reprezentării (de fapt mulţimea de adevăr a lui Inv). Pentru cazul particular
al stivei:
TInv =def { i∈int; v ∈ vector(elm) | Inv〈i,v〉 • 〈i,v〉}
Fie 〈i,v〉 reprezentarea unei stive astfel încât 〈i,v〉 satisface Inv〈i,v〉 ∧ i <
Max, unde i < Max este restricţia suplimentară indusă de Inv asupra operatorului
push. Avem:
unde def(i+1, assign(e, i+1, v)) = true, conform axiomelor (2) şi (5) din
prezentarea TDA vector. Rezultă i+1 ∈ 1..Max ∧ (∀k: 1..i+1 • def(k,
assign(e,k,v)). Reprezentarea noii stive obţinută prin push satisface Inv.
empty(newstack) = true ?
empty(newstack) ⎯ axioma A.1 → empty(Abs〈0,newvec〉)
⎯ axioma A.5 → 0 = 0
⎯ axiomă a lui int → true
empty(push(e,s)) = false ?
empty(push(e,s)) ⎯ s = Abs〈i,v〉 → empty(push(e,Abs〈i,v〉))
⎯ axioma A.2 → empty(Abs〈i+1,assign(e,i+1,v)〉)
⎯ axioma A.5 → i+1 = 0
⎯ Inv → i ≥ 0 ∧ i+1 = 0
⎯ axiome pt. int → false
top(push(e,s)) = e ?
top(push(e,s)) ⎯ s = Abs〈i,v〉 → top(push(e,Abs〈i,v〉))
⎯ axioma A.2 → top(Abs〈i+1,assign(e,i+1,v)〉)
⎯ axioma A.4 → val(i+1,assign(e,i+1,v))
⎯ axioma vector.3 → e
pop(push(e,s)) = s ?
pop(push(e,s)) ⎯ s = Abs〈i,v〉 → pop(push(e,Abs〈i,v〉))
⎯ axioma A.2 → pop(Abs〈i+1, assign(e,i+1,v)〉)
⎯ axioma A.3→ Abs〈i, assign(e,i+1,v)〉
⎯ Re,Inv → Abs〈i,v〉 = s
Principalele operaţii alese aici pentru tipul queue sunt: insq - introducerea
unui nou element la sfârşitul cozii; delq - eliminarea elementului de la vârful cozii;
topq - obţinerea elementului de la vârful cozii (fără eliminarea lui din coadă); emptyq -
testul de coadă vidă; lengthq - numărul de elemente din coadă (lungimea cozii);
concq - concatenarea a două cozi.
delq: queue \ {newq} → queue --- eliminarea primului element din coadă
topq: queue \ {newq} → elm --- selectare prim element din coadă
concq: queue × queue → queue --- concatenare cozi
lengthq: queue → int --- numărul elementelor din coadă
emptyq: queue → bool --- test coadă vidă
P1(q) rezultă din corolarul 2. Pentru verificarea lui P2(q), folosim inducţia
structurală. Cazul newq satisface imediat P2. Fie p o coadă care satisface P2 şi x:
elm. Trebuie să arătăm că q = insq(x,p) satisface P2.
app: elm × List → List --- inserează un element la sfârşitul unei liste
cdr: List \ {nil} → List --- selectează restul unei liste, fără primul element
car: List \ {nil} → elm --- selectează elementul de la începutul unei liste
null: List → bool --- testează dacă o listă este vidă
conc: List × List → List --- concatenează două liste
length: List → int --- calculează numărul de elemente dintr-o listă
conc(l,nil) =
conc(cons(x,ll),nil)⎯ axioma l.8 → cons(x,conc(ll,nil))
⎯ P1conc(ll) → cons(x,ll) = l
Pentru verificarea lui P2conc, prin inducţie structurală, cazul listei vide este:
app(e,conc(l,ll) =
app(e,conc(cons(x,L),ll))
⎯ axioma l.8 → app(e,cons(x,conc(L,ll)))
⎯ axioma l.5 → cons(x,app(e,conc(L,ll)))
⎯ P2conc(L) → cons(x,conc(L,app(e,ll)))
⎯ axioma l.8 → conc(cons(x,L),app(e,ll))= conc(l,app(e,ll))
Cei doi invarianţi arată că o coadă are o reprezentare unică, prin dubletul
〈length(l), l〉, unde l stochează elementele din coadă, în ordinea inserării lor în
coadă.
delq(insq(e,Abs〈i,l〉))
⎯ axioma A.2 → delq(Abs〈i+1,app(e,cons(x,ll))〉)
⎯ axioma l.5 → delq(Abs〈i+1,cons(x,app(e,ll))〉)
⎯ axioma A.3 → Abs〈i,cdr(cons(x,app(e,ll)))〉
⎯ axioma l.3 → Abs〈i,app(e,ll)〉
insq(e,delq(Abs〈i,l〉))
⎯ axioma A.3 →insq(e,Abs〈i-1,cdr(cons(x,ll))〉)
⎯ axioma l.3 → insq(e,Abs〈i-1,ll〉)
⎯ axioma A.2 → Abs〈i,app(e,ll)〉
(4) concq(q,newq) = q ?
concq(Abs〈i,l〉,newq)
⎯ axioma A.1 → concq(Abs〈i,l〉,Abs〈0,nil〉)
⎯ axioma A.4 → Abs〈i,conc(l,nil)〉
⎯ P1conc(l) → Abs〈i,l〉 = q
insq(e,concq(Abs〈i,l〉,Abs〈j,ll〉))
⎯ axioma A.4 → insq(e,Abs〈i+j,conc(l,ll)〉)
⎯ axioma A.2 → Abs〈i+j+1,app(e,conc(l,ll))〉
⎯ P2conc(l) → Abs〈i+j+1,conc(l,app(e,ll))〉
(6) topq(insq(e,q)) = if emptyq(q) then e else topq(q) ?
Caz 6.1) emptyq(Abs〈i,l〉) = true
⎯ axioma A.7 → i = 0 ⎯ Inv → length(l) = 0
⎯ axioma l.9 → l = nil
topq(insq(e,Abs〈i,l〉))
⎯ axioma A.2 → topq(Abs〈i+1,app(e,cons(x,ll))〉)
⎯ axioma l.5 → topq(Abs〈i+1,cons(x,app(e,ll))〉)
⎯ axioma A.5 → car(cons(x,app(e,ll)))
⎯ axioma l.6 → x
topq(Abs〈i,cons(x,ll)〉)
⎯ axioma A.5 → car(cons(x,ll))
⎯ axioma l.6 → x
(7) lengthq(newq) = 0 ?
lengthq(newq) ⎯ axioma A.1 → lengthq(Abs〈0,nil〉)
⎯ axiomaA.6 → 0
queue.ml
let lengthq(Abs(i,l)) = i
and emptyq(Abs(i,l)) = (i = 0);;
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 30
queue.mli
type 'a queue;;
exception queue_error of string;;
value newq: 'a queue
and insq: 'a * 'a queue -> 'a queue
and delq: 'a queue -> 'a queue
and topq: 'a queue -> 'a
and emptyq: 'a queue -> bool
and lengthq: 'a queue -> int
and concq: 'a queue * 'a queue -> 'a queue;;
e , void ins e ∗
en en
∗ ∗
e1 e
ins
e ,
e2 e2 e1
e ∗ del void
en en
∗
e e1 ∗
del
e1 e2
e2
en
∗
e1
top
e1
e2
en en
∗
e1 e1
move
e2 e2
∗
e ∗ move e ∗
− empty:T Ring → bool = {0,1}. empty(R) testează dacă inelul R este vid.
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 32
(1) empty(void) = 1
(2) empty(ins(e,X)) = 0
(3) top(ins(e,X)) = e
(4) del(ins(e,X)) = X
(5) move(ins(e,void)) = ins(e,void)
(6) move(ins(e,ins(e',X))) = ins(e',move(ins(e,X)))
Inelul poate fi implementat ca o listă circulară, iar prelucrarea este banală dacă
limbajul de programare folosit lucrează direct cu pointerii din celulele listei. În acest
caz toate operaţiile sunt în Θ(1). Să considerăm că limbajul de programare folosit nu
lucrează în mod direct cu pointeri ci dispune de tipul T List (listă unidirecţională cu
elemente de tip T), cu operatorii şi specificaţia formală de mai jos, unde:
head: T List\{nil} → T
tail: T List\{nil} → T List
null: T List → bool
(1L) head(cons(e,L)) = e
(2L) tail(cons(e,L)) = L
(3L) null(nil) = 1
(4L) null(cons(e,L)) = 0
inelului, iar L conţine elementele, aranjate în ordine inversă, aflate între poziţia iniţială
şi poziţia curentă a vârfului inelului. Inelul vid este abstractizat prin Absp〈nil,nil〉,
unde operatorul distribuit 〈_,_〉: T1×T2 → T1 T2 Pair construieşte o valoare a tipului
"pereche cu elemente de tip T1 şi T2".
vârf
e1 ek ek+1 en
L R
ek e1 ek+1 en
Fie inelul X = Absp〈L,R〉. Convenim ca lista R să fie vidă doar dacă inelul X
este vid. O astfel de reprezentare a inelului o vom numi normalizată şi poate fi
obţinută prin operaţia:
(norm) normalize〈L,R〉 = if null(R) then 〈nil,reverse(L)〉 else 〈L,R〉
unde operaţia reverse(L) construieşte inversa listei L. Dacă inversarea unei liste
este
reverse(L) = rev(L,nil), cu rev(nil,R) = R
rev(cons(e,L),R) = rev(L,cons(e,R)),
atunci complexitatea operaţiei reverse(L) este Θ(n), n fiind numărul de elemente din
lista L. Aşadar, în cazul cel mai defavorabil, complexitatea operaţiei normalize〈L,R〉
este proporţională cu numărul de elemente din inelul Absp〈L,R〉.
Absp〈nil,cons(e,cons(e',nil))〉 ⎯ move →
Absp〈cons(e,nil),cons(e',nil)〉 ⎯ del →
Absp〈nil,cons(e,nil)〉 ⎯ ins e' →
Absp〈nil,cons(e',cons(e,nil))〉 ⎯ move →
Absp〈cons(e',nil),cons(e,nil)〉
Prin rescrierea celor doi termeni ai axiomei, conform axiomelor tipurilor T Ring,
T List şi ecuaţiilor de implementare, se ajunge la acelaşi inel.
move(ins(e,ins(e',X)))
= move(ins(e,ins(e',Absp〈L,R〉)))
⎯ 2i → move(ins(e,Absp〈L,cons(e',R)〉))
⎯ 2i → move(Absp〈L,cons(e,cons(e',R))〉)
⎯ 4i,1L,2L → Absp(normalize〈cons(e,L),cons(e',R)〉)
⎯ norm → Absp〈cons(e,L),cons(e',R)〉
= X'
ins(e',move(ins(e,X))
= ins(e',move(ins(e,Absp〈L,R〉)))
⎯ 2i → ins(e',move(Absp〈L,cons(e,R)〉))
⎯ 4i,1L,2L → ins(e',Absp(normalize〈cons(e,L),R〉))
Caz 2. R ≠ nil
⎯ norm → ins(e',Absp〈cons(e,L),R〉)
⎯ 2i → Absp〈cons(e,L),cons(e',R)〉
= X'
(1) empty(void) = 1
(2) empty(ins(e,X)) = 0
(3) top(ins(e,X)) = e
(4) del(ins(e,X)) = X
(5) move(ins(e,void)) = ins(e,void)
(6) move(ins(e,ins(e',X))) = ins(e',move(ins(e,X)))
#move(ins(e,void)) ⎯ 5 → #ins(e,void)
#move(ins(e,ins(e',X')))
⎯ 6 → #ins(e',move(ins(e,X')))
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 36
⎯ 11 → 1+ #move(ins(e,X'))
⎯ ip.ind → 1+#ins(e,X')
⎯ 11 → 2+#X'
#ins(e,ins(e',X'))
⎯ 11 → 1+#ins(e',X')
⎯ 11 → 2+#X'
element(i+1,X)
⎯ 7 → elm((i+1) mod #ins(e,void),ins(e,void))
⎯ 11,10 → elm((i+1) mod 1, ins(e,void))
⎯ mod → elm(0, ins(e,void))
⎯ 8,3 → e
LHS = element(i,move(X))
RHS = element(i+1,X)
LHS = element(i,move(ins(e,ins(e',X'))))
⎯ 6 → element(i,ins(e',move(ins(e,X'))))
⎯ 7,11,P1 → elm(i mod (2+#X'),ins(e',move(ins(e,X'))))
Caz (a): i mod (2+#X')=0. Atunci, avem (i+1) mod (2+#X') = 1.
RHS = element(i+1,ins(e,ins(e',X')))
⎯ 7,11 → elm((i+1) mod (2+#X'), ins(e,ins(e',X')))
⎯ [(i+1) mod (2+#X') = 1] → elm(1, ins(e,ins(e',X')))
⎯ 9 → elm(0,ins(e'X'))
⎯ 8,3 → e'
Caz (b): i mod (2+#X')>0. Atunci, i>0 şi i mod (2+#X')= (i-1) mod (2+#X')+1.
top(move0(X)) = top(X)
element(0,X) ⎯ 7 → elm(0 mod #X,X)
⎯ mod → elm(0,X)
⎯ 8 → top(X)
top(movei+1(X)) = top(movei(move(X)))
⎯ ip.ind → element(i,move(X))
⎯ P2 → element(i+1,X)
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 38
void = Void
ins(e,x) = Ins(e,x)
empty(Void) = True
empty(Ins(_,_)) = False
top(Ins(e,_)) = e
del(Ins(_,x)) = x
move(Ins(e,Void)) = Ins(e,Void)
move(Ins(e,Ins(e',x))) = Ins(e',move(Ins(e,x)))
size(Void) = 0
size(Ins(_,x)) = 1+size(x)
p1 x = size(x) == size(move(x))
p2(i,x) = element(i,move(x)) == element(i+1,x)
prop(i,x) = top(move_i(i,x)) == element(i,x)
where move_i(0,x) = x
move_i(i+1,x) = move_i(i,move(x))
-- Un inel
x = make_ring [1,2,3,4,5,6,7,8]
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 39
Aplicaţiile p1(x), p2(i,x) şi prop(i,x) reîntorc valoarea True pentru orice i≥0
si orice inel nevid x.
{-
Axioma elm(i+1,ins(e,x))) = elm(i,x) este rescrisa în stilul
elm(i+1,y) = elm(i,del(y)) folosind schimbarea de variabilă
y = ins(e,x) => x = del(y).
p1 x = size(x) == size(move(x))
p2(i,x) = element(i, move(x)) == element(i+1,x)
prop(i,x) = top(move_i(i,x)) == element(i,x)
move_i(0,x) = x
move_i(i+1,x) = move_i(i,move(x))
-- Un inel
x = make_ring [1,2,3,4,5,6,7,8]
O variantă simplă de sortare a unei liste nevide l constă în construirea unui nou
inel sortat prin inserţia elementului head(l) într-un inel sortat în aceeaşi manieră şi
care conţine elementele listei tail(l). Evident, pentru o listă vidă, inelul sortat este
vid. Operaţia de bază a sortării este
şi satisface axiomele
(p1) put(e,void) = ins(e,void)
(p2) put(e,ins(e',X)) = if e < e' then ins(e,ins(e',X))
else ins(e',put(e,X))
astfel încât:
(o1) ord(void) = 1
(o2) ord(ins(e,X)) = min(e,X) ∧ ord(X)
(m1) min(e,void) = 1
(m2) min(e,ins(e',X)) = e ≤ e' ∧ min(e,X)
Propoziţia 8.4 ∀X∈int Ring • P(X). Demonstraţia este prin inducţie structurală.
Pas de inducţie: X = ins(e',X'), unde e'∈int şi X'∈int Ring sunt valori alese
arbitrar. Ca ipoteză inductivă, considerăm adevărată proprietatea P(e,X') pentru
e∈int ales arbitrar. Să arătăm că proprietatea P(e,X) este adevărată, anume că:
ord(ins(e',X')) ⇒ ord(put(e,ins(e',X')))
(c1) min(e',X') = 1
(c2) ord(X') = 1
ord(put(e,ins(e',X')))
⎯ p2 → ord(ins(e,ins(e',X')))
⎯ o2 → min(e,ins(e',X')) ∧ ord(ins(e',X'))
⎯ m2 → e ≤ e' ∧ min(e,X') ∧ ord(ins(e',X'))
⎯ Caz 1, c1, P"(e,e',X')→ ord(ins(e',X'))1
⎯ ipoteză → 1
Caz 2. e ≥ e'
ord(put(e,ins(e',X')))
⎯ p2 → ord(ins(e',put(e,X')))
⎯ o2 → min(e',put(e,X')) ∧ ord(put(e,X'))
⎯ ip.induct. P(e,X'), c2 → min(e',put(e,X'))2
⎯ P'(e',e,X') → e' ≤ e ∧ min(e',X')
⎯ Caz 2, c1 → 1
Considerând lista de sortat l = cons(l1,cons(l2,...,cons(ln,nil)...)),
atunci, prin inducţie după n şi conform propoziţiei (2.11), inelul
r = put(l1,put(l2,...,put(ln,void)...)
1
Termenul min(e,X') este adevărat conform implicaţiei P"(e,e',X')
2
Termenul ord(put(e,X')) este adevărat conform implicaţiei P(e,X')
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 42
Dacă dorim ca rezultatul sortării să fie o listă, inventăm o funcţie care descarcă
elementele unui inel (indiferent de tipul elementelor) într-o listă iniţial vidă.
put(e,x)
| empty(x) = ins(e,void)
| e < top(x) = ins(e,x)
| otherwise = ins(top(x),put(e,del(x)))
ring_to_list x
| empty(x) = []
| otherwise = (top x):(ring_to_list (del x))
3
Motivul îl constituie definirea constructorilor void şi ins ca funcţii implementate pe
baza constructorului Abs. Haskell nu acceptă ca parametrii formali ai funcţiilor să fie specificaţi
în forma unor aplicaţii de funcţii, aşa cum cer axiomele nemodificate ale lui put şi
ring_to_list. Totodată, codificarea din modulul RingSort are un avantaj: funcţionează
corect cu ambele implementări ale inelului, conforme modulelor T_Ring şi RingImpl.
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 43
ring_to_list x
| empty(x) = []
| otherwise = (top x):(ring_to_list (del x))
Modulele Haskell de mai sus implementează mai mult decât sortarea unei liste
de întregi. Ele descriu sortarea unei liste cu elemente de orice tip peste care este
definită o relaţie de ordine. Generalitatea este vizibilă din signatura Haskell a funcţiei
sort, sintetizată automat, anume:
Signatura arată că tipul t trebuie să facă parte din clasa Ord, clasa tipurilor
peste care este definită o relaţie de ordine. Astfel, se poate scrie:
4
foldr cere ca put să fie o funcţie binară. În Haskell, put(e,x) defineşte o funcţie cu
un singur parametru din mulţimea produs int × int Ring, în timp ce put e x descrie o funcţie
binară cu primul parametru de tip int şi al doilea parametru de tip int Ring. Diferenţa este
mai subtilă: o funcţie binară poate fi aplicată parţial. Aplicarea asupra primului parametru
produce o funcţie unară care aşteaptă cel de al doilea parametru. Din program, se vede că
însăşi foldr este o funcţie ternară care are proprietăţi similare.
Cristian Giumale/ Sisteme de tipuri şi programare funcţională/ Note de curs 44
Referinţe
Diller Antoni. (1994). Z: An Introduction to Formal Methods, (2nd edition), John
Wiley & Sons, Inc.
Stănăşilă Octavian. (1978). Elemente de matematică discretă, Litografia
Universităţii Politehnice Bucureşti.
Turner John and McCluskey T. Lee. (1994). The Construction of Formal
Specifications, An Introduction to the Model-based and Algebraic
Approaches, McGraw-Hill International Series in Software Engineering.