programare functionala. haskell
TRANSCRIPT
-
8/9/2019 Programare Functionala. Haskell
1/206
MM
Mihai Gontineac
Haskell B. Curry (1900-1982)
Alexandru Myller
Iai
-
8/9/2019 Programare Functionala. Haskell
2/206
EDITURA ALEXANDRU MYLLERIai, B-DUL CAROL I, nr.11,
tel. 0232-201061 / fax. 0232-201060
http://www.math.uaic.ro/~sm/
Descrierea CIP a Bibliotecii Naionale a Romniei
GONTINEAC, MIHAI
Programare funcional : o introducere utiliznd limbajulHaskell / Mihai Gontineac. - Iai : Editura Alexandru Myller, 2006Bibliogr.
ISBN (10) 973-86987-6-6 ; ISBN (13) 978-973-86987-6-6
004.43
Referent tiinificLect. Univ. Dr. Dnu Rusu
Facultatea de Matematic
Universitatea Alexandru Ioan Cuza Iai
Toate drepturile asupra acestei ediii aparin autorului i Editurii Alexandru Myller
-
8/9/2019 Programare Functionala. Haskell
3/206
Memoriei tatlui meu
-
8/9/2019 Programare Functionala. Haskell
4/206
-
8/9/2019 Programare Functionala. Haskell
5/206
5
Cuprins
Introducere .................................................................................................... 7
Elemente ale lambda calculului ................................................................... 131. Importana ................................................................................................ 132. Lambda Calcul ........................................................................................ 143. Egalitate i normalizare............................................................................ 234. Codificarea datelor in lambda calcul........................................................ 305. Scrierea funciilor recursive n lambda calcul................................................... 35
Limbajul Haskell 98 descriere general ................................................. 411. Istoric.................................................................................................................. 412. Proprieti caracteristice..................................................................................... 443. Implementri i instalare .................................................................................. 48
4. Scrierea codului surs ....................................................................................... 505. Un tur rapid al funciilor din Prelude ........................................................... 53
Bazele limbajului Haskell.............................................................................. 871. Aritmetic n Haskell........................................................................................ 872. Upluri .............................................................................................................. 893. Liste ................................................................................................................ 904. Crearea fiierelor proprii ................................................................................. 955. Funcii .............................................................................................................. 986. Recursivitate .................................................................................................... 1037. Interaciune .................................................................................................... 110
Tipuri de baz .............................................................................................. 1171. Tipuri simple ................................................................................................. 1182. Tipuri polimorfe ............................................................................................. 1213. Clase de tipuri ............................................................................................... 1234. Tipuri funcionale .......................................................................................... 1255. Tipuri de date utilizator.................................................................................. 132
Alte proprieti i tehnici utile ................................................................... 1351. Module ............................................................................................................ 1352. Declaraii locale................................................................................................ 1342. Aplicare parial ............................................................................................... 136
-
8/9/2019 Programare Functionala. Haskell
6/206
6
3. Potrivirea abloanelor sau Pattern Matching ............................................... 1374. Grzi ................................................................................................................ 1405. Clase................................................................................................................. 1426. Comprehensiunea listelor ................................................................................ 1507. Din nou despre tipuri ...................................................................................... 154
Anex (Exemple de programe n Haskell)................................................ 164
Bibliografie ................................................................................................. 203
-
8/9/2019 Programare Functionala. Haskell
7/206
7
Introducere
n cele ce urmeaz, vom ncerca s oferim motivaia abordrii unui
model total diferit de ceea ce se nelege, de obicei, prin programare.
O dat ce software-ul devine din ce n ce mai complex, este foarte util
i indicat s-l structurm ct mai bine. Software-ul care este bine structurat este
uor de scris, uor de corectat i ne furnizeaz multe module care se pot
reutiliza pentru reducerea costurilor de programare.
Numele de programare funcional provine din faptul c un program
este constituit n ntregime din funcii. nsui programul principal este scris ca
o funcie care primete intrarea ca argument i furnizeaz ieirea ca rezultat. De
obicei, funcia principal este definit n termeni de alte funcii, care la rndul
lor sunt definite n termeni de alte funcii, .a.m.d. pn la funciile de baz,
care constitue primitivele limbajului.
Programele n limbajele tradiionale, cum ar fi FORTRAN, C, Pascal,
se bazeaz n mod esenial pe modificarea valorilor unei colecii de variabile,
numitstare. Cu excepia cazurilor n care un program poate rula n mod
continuu (de exemplu un controler al unui proces de producie), putem
rezuma totul n urmatoarea abstractizare:
nainte de execuie, sistemul se afl n starea iniial, s0, ce reprezint
intrarea programului (colecia datelor de intrare, de exemplu), i, n momentul
n care programul s-a terminat, starea are o valoare nous, care include i
-
8/9/2019 Programare Functionala. Haskell
8/206
8
rezultatele. Mai mult, execuia programului se face prin executarea unor
comenzi, fiecare comanda schimbnd starea. n acest fel, obinem un ir de
tranziii prin diferite stri:
'ssssss n =...= 210
Starea se modific, n mod uzual, prin comenzi de atribuire (asignare),
scrise de obicei sub forma v=E sau v:=E, unde v este o variabili E este o
expresie. Comenzile se execut n manier secvential prin scrierea lor una
dupa alta n cadrul programului, separate de obicei prin ;. Programul conine
i o serie de instruciuni asupra modului n care sunt fcute aceste schimbri de
stare i, din acest motiv, acest stil de programare se numete procedural sau
imperativ. n mod corespunztor, limbajele care suport acest tip de
programare se numesc limbaje procedurale sau imperative (cum ar fi C, C++,
Fortran, Pascal, .a.).
Programarea funcional reprezint o desprire clar de acest model de
programare. n mare, un program funcional este o expresie iar execuia
nseamnevaluarea expresiei (din acest motiv, programarea funcionala se mai
numete iprogramare aplicativdeoarece mecanismul de baz este aplicarea
unor funcii argumentelor lor).
Presupunnd c un program imperativ (ca un ntreg) este determinist,adic ieirea sa este complet determinat de intrare, putem spune c starea
final este funcie de starea initial, adic s'=f(s0) sau, nc Output =
Program(Input).
n programarea funcional, aceast modalitate este pus mai tare n
eviden: un program nu este altceva dect o expresie ce corespunde unei
funcii matematicef. Limbajele funcionale suport construcia acestor expresii
datorit unor componente funcionale extrem de puternice.
-
8/9/2019 Programare Functionala. Haskell
9/206
9
Programarea funcional poate fi comparat cu cea imperativi n sens
negativ i n sens pozitiv.
Astfel, n sens negativ, programele funcionale stricte nu utilizeaz
variabilele - nu exist stri. Ca i consecin, ele nu pot utiliza atribuirea,
ntruct nu exist nimic cruia sa i se poat atribui ceva. Drept urmare,
ideea execuiei comenzilor n serie nu are sens, intruct prima comand
poate s nu fac difereniere de o a doua deoarece nu exist stri care s
fac legtura dintre ele (exist totui i o modalitate de manipulare sec-
venial a variabilelor, pe care o vom ntlni pe parcursul acestei cri).
n mod pozitiv, programele funcionale pot utiliza funciile ntr-un mod
mult mai sofisticat. Funciile pot fi tratate n acelai mod n care tratm
orice alt obiect, orict de simplu ar fi el; ca i n cazul obiectelor intregi,
ele pot fi date altor funcii ca argumente i, n general, se poate calcula
cu ele. n loc de iniruire i cicli, programele funcionale utilizeaz
funciile recursive, adic funciile care sunt definite n funcie de ele
nsele. Prin contrast, majoritatea limbajelor tradiionale au facilitai
srace n acest sens. C - ul permite manipularea limitat a funciilor cu
ajutorul pointerilor, ns nu permit crearea funciilor n mod dinamic, iar
FORTRAN - ul nici mcar nu suport recursia.Pentru ilustrarea modului n care se poate face distincia ntre
programarea funcionali cea imperativ, s dm exemplul funciei factorial.
Codat imperativ n C, fr a utiliza atribuiri neuzuale, ea ar aprea
astfel:
int fact(int n)
{ int x = 1;
-
8/9/2019 Programare Functionala. Haskell
10/206
10
while (n > 0)
{ x = x * n;
n = n - 1;
}
Aceeai funcie se poate da i ca o funcie recursiv, de exemplu,
utiliznd Haskell:
factorial 1 = 1
factorial n = n * factorial (n - 1)
Vom reveni asupra unor astfel de exemple.
Probabil c principalul motiv pentru care utilizm programele
funcionale este c ele corespund mult mai direct obiectelor matematice, i este
mai uor s le inelegem. Un alt avantaj potenial ar fi faptul c evaluarea
expresiilor se face fr efecte secundare n orice stare, adic subexpresii
separate pot fi evaluate n orice ordine fr s se influenteze una pe cealalta.
Aceasta ne conduce la concluzia c programele funcionale se preteaz uor la
implementare paralel. Un alt motiv este faptul c algoritmii n Haskell se scriu
foarte asemntor cu descrierea lor n limbaj natural.
Lucrarea realizeaz o mic introducere n ceea ce se vrea a fi modelul
de programare funcional. Dup o scurt introducere n lambda calcul, adic
fundamentul teoretic al acestui model de programare, vom ncepe studiul
posibilitilor oferite de programarea funcional cu ajutorul limbajului
Haskell. Acesta este unul din limbajele cele mai reprezentative pentru
programarea funcional.
Din pcate, o serie de faciliti i tehnici foarte importante din Haskell
nu i-au putut gsi locul n aceast carte, ntruct, pentru nelegerea complet a
-
8/9/2019 Programare Functionala. Haskell
11/206
11
lor, ar fi fost nevoie de o introducere teoretic ceva mai lung i, mai ales,
pentru tinerii cititori, cartea ar fi putut deveni plicticoas. Vom ncerca, ct
de curnd, s oferim cititorilor interesai o continuare a acestei introduceri.
Pn acum s-ar putea spune c Haskell ar fi limbajul perfect. ntrebarea,
natural de altfel, este: de ce nu este utilizat peste tot? Principalul motiv ar fi c
limbajele funcionale, n general, i Haskell n particular, nu sunt nc att de
cunoscute. ns, de curnd (n acest an), a aprut Visual Haskell cu versiunile
0.0 i 0.2 construit pe baza GHC 6.6. Putem spune c, odat cu apariia sa,
ncepe o nou er pentru programarea funcional i asta datorit faptului c
Visual Haskell este integrat n Visual Studio .NET 2003 i Visual Studio .NET
2005, principalele medii de dezvoltare software n momentul de fa.
Cartea are ca baz cursul opional de Introducere n Programarea
Funcional inut la anul III, secia Matematic-Informatic, Facultatea de
Matematic din Universitatea A. I. Cuza, Iai.
-
8/9/2019 Programare Functionala. Haskell
12/206
12
-
8/9/2019 Programare Functionala. Haskell
13/206
13
Capitolul I
Elemente ale lambda calculului
Cititorii care sunt obisnuii cu programarea imperativ, vor vedea
tranziia ctre programarea funcional ca fiind, n mod inevitabil, destul de
dificil. Am ales s introducem mai nti lambda calculul, aratnd modul ncare el poate fi vzut ca fundament teoretic al limbajelor funcionale (i asta
chiar dac unii cititori vor fi nerbdtori s nceap repede programarea
real).
1 Importana
Importana -calculului n programarea funcional i n informatica
general
este dat
de cteva propriet
i caracteristice:
Se pot modela mecanismele de chemare a funciilor de urmtoarele
tipuri: call-by-name, call-by-value i call-by-need (ultimele dou mai
sunt cunoscute i ca evaluare strictrespectiv evaluare ntrziat).
-calculul este Turing universal, adic are aceeai putere ca a mainii
Turing universale; este, probabil, cel mai natural model de calcul. Teza
lui Church afirm c funciile calculabile sunt exact acele funcii care
se pot reprezenta n -calcul .
-
8/9/2019 Programare Functionala. Haskell
14/206
14
Noiunile de confluen, terminareiformnormaldin acest model de
calcul se aplic n teoria rescrierii (rewriting theory) .
Lisp, unul din primele limbaje de programare importante, a fost inspirat
de -calcul .
-calculul (ca i extensiile sale) se poate folosi n dezvoltarea unor
sisteme mai bine scrise, prin utilizarea, de exemplu, a polimorfismului.
datorit proprietilor sale, se poate utiliza n investigarea unor chestiuni
teoretice din programare, cum ar fisinteza unui program .
Semantica denotaionala, care este o metod important pentru
specificarea formal a limbajelor de programare, utilizeaz -calculul
pentru notaiile sale.
2 LambdaCalcul
Introducere
Lambda calculul este bazat pe aa numita notaie lambdapentru notarea
funciilor. n matematica informal, atunci cnd cineva dorete s fac referire
la o anumit funcie, i d mai intai un nume arbitrar dupa care utilizeaz acel
nume. Marea majoritate a limbajelor de programare prezint acelai aspect:definim funciile dndu-le un nume.
n orice caz, acest tip de notaie devine greoi atunci cnd apar funcii de
ordin nalt. Dac dorim s tratm funciile ca pe orice alt obiect matematic,
devine inconsistent s insistm pe nume. n acest sens avem drept exemplu
expresiile aritmetice, care sunt obinute, la rndul lor, din altele mai simple. Pur
i simplu scriem subexpresiile fr a fi necesar s le dm un nume.
-
8/9/2019 Programare Functionala. Haskell
15/206
15
Imaginai-v ce ar insemna s utilizm expresiile aritmetice n modul urmtor:
Definimx,y,zprinx = 3,y = 5 iz= 4. Atunci 222 zyx =+ .
Notaia lambda permite notarea funciilor n acelai mod ca orice alt
categorie de obiecte matematice. Din acest punct de vedere, exist deja onotaie utilizat n acest scop, n matematic, chiar dac ea este utilizat ca o
parte a definiiei unui nume temporar. Putem scrie:
xaf(x)
pentru a considera funcia care duce argumentulx ntr-o expresie arbitrarf(x),
care poate sau nu s conin pe x. Pentru asta vom utiliza o alt notaie,
introdus de A. Church (1941):
x.f[x]
care se citete n acelai mod. De exemplu, x.x este funcia identitate, x.xn
este ridicarea la putere, .a.m.d..
Din punct de vedere anecdotic, trebuie s subliniem faptul c simbolul
nu are nici o semnificaie i alegere lui este ntmpltoare. Ea a aprut datorit
dificultii scrierii x .f[x] (cum apruse la nceput n manuscris). ntruct
dactilograful nu putea scrie la maina de scris n acest mod, el a scris x.f[x],
care, n minile altui dactilograf, a devenit x.f[x].
Utiliznd notaia lambda, se pot clarifica unele confuzii generate de
notaia matematic. De exemplu, se obisnuiete s se scrie f(x), lsnd
contextului s decid dac este vorba de funcia nsi, sau de rezultatul
aplicrii ei ntr-un x particular. Dac pornim cu variabile i constante, i
construim expresii utiliznd -abstractizarea i aplicarea funciilor argumen-
telor lor, putem reprezenta expresii matematice foarte complicate.
-
8/9/2019 Programare Functionala. Haskell
16/206
16
Vom utiliza notaia convenionalf(x) pentru aplicarea funciei f n
argumentul x; singura deosebire este c, n notaie lambda, parantezele se pot
omite, i putem scriefx. Se presupune c aplicarea funciei ncepe de la stnga,
adicfxy inseamna (f(x))(y). Drept prescurtare pentru x.y.f(x,y) vom scrie
xy.fxy, i aa mai departe.
De asemenea, se presupune c abstractizarea cu lambda se extinde la
dreapta ct de mult este posibil. n aceast manier, x.xy nseamnx.(xy) i
nu (x.x)y.
La o prim vedere, s-ar prea c avem nevoie de o notaie speciala pentru
funcii de mai multe variabile. Avem nsa o metod, numitmetoda Curry (sau
procedeul de curry-zare), dup numele logicianului Haskell B. Curry. Ideea de
baz poate fi privit i ca provenind din teoria categoriilor, mai exact din
adjuncie. Fr a intra ns, acum, n prea multe amnunte, putem transforma
orice funcie de dou variabile ABC, ntr-o funcie de o variabil,
A(BC) =ACB.
De exemplu, s considerm expresia de xy.x+y. Ea poate fi vzut, n
virtutea comentariului precedent, ca o funcieR(RR); se spune despre ea
c este o funcie de ordin superior, sau o funcional, ntruct odat aplicat
unui argument ea produce o alt funcie, care accept urmtorul argument. n
acest mod, argumentele se iau unul dup altul i nu amndou odat:
(xy.x+y)1 2=(y.1+y)2=1+2
Definiia 1.Termii-calculului, numii i -termi (sau simplu termi), se
construiesc recursiv dintr-o mulime de variabilex,y,z,.... Ei pot lua una din
formele urmtoare:
xvariabil
-
8/9/2019 Programare Functionala. Haskell
17/206
17
(x.M) abstractizarea termilor, unde Meste term
(MN) aplicarea termilor; n acest mod se noteaz rezultatul aplicarii
funciei notate prin Mfunciei notate prinN.
Vom utiliza litere mari , L,M,N,... pentru termi. Vom scrie M N pentru asublinia faptul c M i N sunt -termi identici. Vom discuta mai trziu
egalitatea termilor.
Aceast definiie a termilor ne arat c, n principal, proprietile lor se
pot demonstra prin inducie. n plus, se poate descrie sintaxa lambda termilor,
n acelai mod n care se descrie sintaxa oricrui limbaj de programare:
Exp = Var | Const | Exp Exp | Var.Exp
2 Legarea variabilelor i substituia.
n orice term (x.M) , x poart numele de variabil legat iar M este
corpul termului. Orice apariie a lui x in M este legat prin abstractizare. O
apariie a unei variabile se numete liber, dac nu este legat printr-o
abstractizare care s o conin. De exemplu, x apare legat i y apare liber n
(z.(x.(xy)).
Variabile libere i variabile legate au existat din totdeauna n matematic.
De exemplu:
- n ba dxxyf )( , variabilax este legat iar variabilay este liber;- n suma )(
0
kpn
k
=
, variabila keste legat;
- Cuantificatorii logici i sunt de utilizai n matematic pentru legareavariabilelor.
-
8/9/2019 Programare Functionala. Haskell
18/206
18
Abstractizarea (x.M) reprezint funcia f avnd proprietatea cf(x)=M
pentru toi x. Aplicarea lui f in N conduce la substituirea cu N a tuturor
apariiilor luix n M. Astfel, (x.x)E=E.
Fie Mun term.
Definiia 2. Mulimea tuturor variabilelor legate din M, BV(M), se definete
prin:
BV(x) = ;
BV(x.M) =BV(M) {x};
BV(MN) =BV(M) BV(N).
Definiia 3. Mulimea tuturor variabilelor libere din M, FV(M), se definete
prin:
FV(x) = {x};
FV(x.M) =FV(M) \ {x};
FV(MN) =FV(M) FV(N).
Definiia 4. DacL,M sunt -termi, rezultatul substituirii cuL a tuturor
apariiilor libere ale luiy n M, este dat prin:
restin
daca
]/[ x
xyL
yLx
restin])/[.(
daca.]/)[.(
yLMx
xyMxyLMx
(MN)[L/y] (M[L/y]N[L/y])
Notaiile definite anterior nu fac parte, ele nsele, din -calcul. Ele
aparin meta-limbajului utilizat pentru a vorbi despre -calcul.
-
8/9/2019 Programare Functionala. Haskell
19/206
19
Atenie: atunci cnd se face o substituie nu trebuie deranjat legarea
variabilei. Spre exemplificare, consideram term-ul (x.(y.x)). Acest term ar
trebui s reprezinte o funcie care, atunci cnd ar fi aplicat unui argumentN,
conduce la funcia constant (y.N). Din pcate, acest lucru nu funcioneaz n
cazul n care Ny; noi am definit substituia prin (y.x)[y/x](y.y). Apariia
liber a lui x devine o apariie legat a lui y - un exemplu de captur a
variabilei. Dac s-ar permite aa ceva, -calculul nu ar fi consistent.
Substituia M[N/x] este sigur, dac mulimea variabilelor legate ale lui
Meste disjunct de cea a variabilelor libere ale luiN:
BV(M)FV(N)=
Pentru a putea face substituii sigure, este indicat s se opereze mai nti o
redenumire a variabilelor legate ale lui M(bineneles, numai dac acest lucru
este necesar), astfel nct s fie indeplinit condiia precedent. Pentru
exemplul prezentat, putem schimba mai nti (y.x) n (z.x), dup care se
obine substituia corect (z.x)[y/x]=(z.y).
3 Conversii
Exist trei tipuri de conversii utilizate n -calcul pentru a transforma un
term ntr-un altul. Numele acestor conversii (primele dou aprnd nconsideraiile lui A. Churh cu alt denumire) a fost dat de Haskell Curry.
-conversia ])/[.().( xyMyMx ;
ea redenumete abstractizarea variabilei legate de la x la y. Se poate realiza
numai n cazul n carey nu apare n M. n general se va ignora distincia ntre
termi care pot fi fcui identici printr-o -conversie. Mai mult, este clar c
aceast conversie nu este unidirectionala.
-
8/9/2019 Programare Functionala. Haskell
20/206
20
-conversia ]/[)).(( xNMNMx ;
ea substituie argumentul, N, n corpul abstractizrii, M. Pentru ca aceast
conversie s fie sigur trebuie caBV(M)FV(N)=.
- conversia MMxx )).(( ;
orice abstractizare (x.(Mx)) pentru carex nu apare ca variabil liber n Mse
poate reduce la M.
Dintre toate conversiile menionate, -conversia este cea mai important din
punct de vedere al programrii. n timp ce -conversia este, din punct de
vedere tehnic, numai o schimbare a numelor variabilelor legate, - conversia
este o forma de extindere, de interes pentru partea logic a lambda calculului.
4 Reduceri
Pn acum am vorbit despre termi identici i nu am pomenit absolut
nimic despre o egalitate a lor. Consideraiile acestei seciuni vor fi utilizate n
acest scop. n general, datorit faptului c-conversia nu este unidirecional,
ea se ignor.
Definiia 5. Spunem c term-ul Mse reduce simplu la term-ulN(sau cNesteobinutdin M printr-o reducere simpl ), dac NM sau NM . Vom
nota acest lucru prin NM . Dac un term nu admite nici o reducere, vom
spune c el este n form normal. Un term admite o form normal dac se
poate reduce la un term aflat n form normal printr-un numr finit de reduceri
simple. A normaliza un term nseamn a aplica reduceri simple pna se obine
un term aflat n form normal.
-
8/9/2019 Programare Functionala. Haskell
21/206
21
Vom introduce, n mod formal, regulile de inferenpentru :
Dac 'MM atunci )'.().( MxMx ;
Daca 'MM atunci )'()( NMMN ;
Daca 'MM atunci )'()( LMLM .
Exemple.
1. Termii yx.y sixyzsunt in forma normala.
2. (x.x)y nu este n form normal; forma sa normala estey.
3. (x.xx)(x.xx) se reduce prin -conversii la el nsusi. Chiar dac nu este
afectat de reducere, el nu este n form normal. Acest term se numete,
de obicei, . Vom mai avea prilejul s vorbim despre acest term.
5 Curry-zarea funciilor
Aceast tehnic a fost introdus de Schnfinkel dup numele lui Haskell
B. Curry; ea a aprut datorit faptului c -calculul are numai funcii de un
singur argument. O funcie avnd mai multe argumente se exprim, cu ajutorul
acestei tehnici, ca o funcie avnd un singur argument i care ia valori tot
funcii.S considerm cL este un term, avnd dou variabile libere x i y, i
dorim s formalizm funciaf(x,y) =L. Abstractizarea (y.L) conine variabila
liber x; pentru fiecare x, se obine o funcie n variabila y. Aadar
abstractizarea (x.(y.L)) nu conine variabile libere; atunci cnd se aplic
argumentelorMiN, rezultatul se obine inlocuindx cu Miy cuNnL. Din
-
8/9/2019 Programare Functionala. Haskell
22/206
22
punct de vedere simbolic, vom face -conversii, i vom ignora eventualele -
conversii:
(((x.(y.L))M)N) ((y.L[M/x])N) L[M/x][N/y]
Este clar c aceast tehnic se poate aplica pentru orice numar de
argumente ale funciilor.
Funciile curry-zate se bucur de o mare popularitate n programarea
funcional ntruct se pot aplica ctorva dintre primele argumente obinnd
alte funcii care sunt, ele nsele, de interes.
6 Convenii de utilizare a parantezelor
Scrierea cu prea multe paranteze este destul de greoaie i este generatoare
de erori. Din acest motiv, se fac o serie de simplificri sau abrevieri pentru a
uura scrierea. De exemplu, vom face urmtoarele abrevieri:
(x1.(x2.(...( xn.M)...))) se abreviaz prin (x1x2...xn.M)
(...(M1M2)... Mn) se abreviaz prin (M1M2...Mn)
n fine, renunm la parantezele care se afl la exteriori la acelea care
conin corpul abstractizrii. De exemplu:
(x.(x(y.(yx)))) se poate scrie x.x(y.yx)
Trebuie acordat foarte mult atenie modului de aezare a parantezeloratunci cnd este nevoie. Am putea, de exemplu, s avem reducerea
z.(x.M)N)z.M[N/x]
ns un termen asemanator, z.z(x.M)Nnu admite alte reduceri dect acelea
care apar n interiorul termilor M i N, deoarece x.Mnu se aplic nici unui
term. Pe de alt parte, o reducere pe care am fcut-o mai sus (cnd am explicat
curry-zarea) arat, fr paranteze, astfel:
-
8/9/2019 Programare Functionala. Haskell
23/206
23
(x.y.L)M)N) (y.L[M/x])NL[M/x][N/y]
S mai observm c, din punct de vedere notaional, x.MNabreviazx.(MN)
i nu (x.M)N. De asemenea, n mod uzual,xyzabreviaz (xy)zi nux(yz).
3 Egalitate i normalizare
-calculul este, n esen, o teorie ecuaional, adic este constituit din reguli
care s demonstreze egalitatea a doi -termi. Una din proprietile cheie n
acest sens este c doi termi sunt egali dac se pot reduce la un acelai term.
Reducerea n mai muli pai.
n general, cnd scriem MN, se intelege faptul cMse reduce laN
dup exact unsingurpas de reducere. De obicei, vom fi interesai de
posibilitatea reducerii dupa un numar oarecare de pa, i vom scrie M* N
dac existk 0 astfel nct s avem:
MM1M2 ... Mk N
Notaia nu este ntmpltoare, fiind clar c relaia * este nchiderea
reflexivi tranzitiv a relaiei .
Exemplu. ((z.(zy))(x.x)) * y.
Egalitatea -termilorPrin extindere vom nelege relaia invers reducerii,
Din punct de vedere informal, doi termi M,M' sunt egali dac unul se
poate obine din cellalt printr-un numr finit de transformri (adic reduceri
sau extinderi). Putem exprima acest lucru, figurativ, n modul urmtor:
-
8/9/2019 Programare Functionala. Haskell
24/206
24
Exemplu. Avem a((y.by)c)=(x.ax)(bc) intruct ambii termi se reduc la a(bc).
Observaii.
Relaia de egalitate este relaia de echivalen generat de , adic
(-1)*. Din punct de vedere intuitiv, faptul cM= M' nseamn cM
i M' au aceleai valori.
n plus, relaia de egalitate satisface legile de a fi o congruen pentru
fiecare dintre modurile de construcie a -termilor. Astfel, dacM= M'
atunci au loc
(x.M)=(x.M');
(MN)=(M'N);
(LM)=(LM').
Teorema Church-Rosser
Din punct de vedere intuitiv, aceast teorem demonstreaz faptul c-
calculul este confluent, adic, pornind de la un acelai -term, nu exist dou
iruri de transformri care s ne conduc la forme normale distincte. Cu alte
cuvinte, forma normal a unui term este independent de ordinea n care sunt
efectuate reducerile.
Teorema 1. Daca M=Natunci existL astfel nct M* LiN* L.
M M1 M2 ... Mk-1 Mk = M
N1 N2 ... Nk
* * * * * *
-
8/9/2019 Programare Functionala. Haskell
25/206
25
Exemplu. S considerm urmatorul term (x.ax)((y.by)c). Putem s-i aplicm
reducerile pentru a ajunge la forma normal n dou moduri. Subtermii asupra
crora vom aplica transformri apar ngroai:
(x.ax)((y.by)c)a((y.by)c) a(bc)
(x.ax)((y.by)c) (x.ax)(bc)a(bc)
Consecine.
Daca M=NiNeste n form normal, atunci M* N; adic, dac un
term se poate transforma n form normal utiliznd reduceri i
expansiuni, atunci forma normal se atinge numai prin reduceri.
Daca M=Ni ambii termi MiNsunt n form normal, atunci MN(pn la o redenumire a variabilelor legate). Reciproc, dacMiNsunt
n form normal i sunt distincti atunci MN, adic nu exist nici o
modalitate s transformm MnN. De exemplu, xy.x xy.y.
Observaie. O teorie ecuaionala este inconsistentdac toate ecuaiile sale se
pot demonstra. Datorit teoremei Church-Rosser, putem spune c-calculul
este consistent, intruct nu exist nici o posibilitate de obinere a unor forme
normale distincte prin aplicarea unor strategii distincte de transformare. Fr
aceast proprietate, -calculul nu ar fi avut nici o relevan n calcul.
Proprietatea Diamant
Unul dintre paii cei mai importani n demonstrarea teoremei Church-
Rosser este demonstrareaproprietii diamant:
-
8/9/2019 Programare Functionala. Haskell
26/206
26
Teorema 2. DacM*M1i M*M2 atunci exist un termL astfel nct s
avem M1Li M2L.
Putem vizualiza teorema prin diagrama:
Aceast proprietate se poate demonstra utiliznd o fragmentare,
considernd mai nti cazul n care MM1 (ntr-un singur pas) i M*M2
(n mai muli pai). Aceste fragmente se lipesc apoi pn la completarea
diamantului. Detaliile, n schimb, implic o analiz laborioas pe cazuri
posibile de reducere datorate diverselor tipuri de reducere pentru diferitele
tipuri de termi. Pentru cei interesai, recomandm parcurgerea bibliografiei de
la sfritul acestui capitol.
n orice caz, urmatoarea diagram ilustreaz principiul de funcionare a
proprietii diamant:
L
M
*
M1 M2
* *
*
-
8/9/2019 Programare Functionala. Haskell
27/206
27
Posibilitatea neterminrii
Chiar dac, aplicnd dou iruri distincte de transformri, nu se pot
obine forme normale diferite ale unui acelai term, ele pot furniza
comportamente diferite: unul poate s se opreasc, n timp ce cellalt va lucra
la nesfrit. n mod uzual, dac M are o form normal i admite un ir de
reduceri infinit, el va conine un subtermL care nu are o form normal; acest
subterm poate fi ters printr-o reducere.
Sa ne amintim c termul (x.xx)(x.xx) se reduce la el insui.
Reducerea (y.a) a i atinge forma normal prin tergerea lui .
Aceasta corespunde aa numitei metode call-by-name de utilizare a
funciilor: argumentul funciei nu se reduce ci se substitue aa cum este el n
corpul abstractizrii.
ncercnd s normalizm mai nti argumentul, n termul anterior, se
genereaz un ir de reduceri care nu se poate termina:
M M1 M2 ... Mk-1 Mk = M
N1 N2 ... Nk* * * * * *
L1 L2 ... Lk-1
K1 .....
E
* * * *
* *
-
8/9/2019 Programare Functionala. Haskell
28/206
28
(y.a) (y.a) (y.a) ...
Evaluarea argumentului naintea substituirii n corpul abstractizrii
corespunde aa-numitei metode call-by-value de utilizare a unei funcii. n
exemplul prezentat, aceast strategie nu conduce niciodat la o form normal.
Ordinea normal de reducere
Metoda de reducere n ordine normal este ca, la fiecare pas, s se
efectueze -reducerea cea mai din stnga i cea mai exterioar (-reducerile
pot fi lsate la sfrit). Cea mai din stnga nseamn, s reducemL naintea lui
Nn termulLN. Cea mai exterioarnseamn, de fapt, s se reduc (y.M)N
nainte de a reduce MsauN.
Ordinea normala de reducere corespunde evaluarii call-by-name.
Se poate demonstra c, dac exist form normal, atunci ea poate fi atins
prin ordinea normal de reducere. n orice caz, putem observa c, reducnd
termulL mai inti, n termul LN, se poate transformaL ntr-o abstractizare, de
exemplu x.M. Reducerea lui (x.M)N, poate tergeN.
Evaluare ntrziat (lazy evaluation)
Din punct de vedere teoretic, ordinea normal de reducere este optimal,n sensul c ofer forma normal ori de cte ori ea exist. Pentru calculul
practic, ns, ea nu este prea eficient.
S presupunem c avem deja o codificare n lambda calcul numerelor
naturale (vom vedea n urmtoarea seciune cum codificm diferite tipuri de
date n -calcul) i s definim funcia de ridicare la ptrat prin
sqr n.mult nn.
-
8/9/2019 Programare Functionala. Haskell
29/206
29
Atunci
sqr(sqrN) mult(sqrN)(sqrN) mult(multNN)(multNN)
i va trebui s se evalueze patru copii ai termuluiN.
Evaluarea call-by-value ar fi evaluat N nainte de orice, o singur dat,
dar, dup cum s-a vzut deja, exist posibilitatea neterminrii.
Lazy evaluation (evaluarea ntrziat), mai este numiti evaluare call-
by-need. Ea nu evalueaz un argument de mai multe ori. Un argument se
evalueaz doar atunci cnd valoarea sa este cerut sa produc un rspuns; chiar
i atunci, argumentul se evalueaz doar n punctul n care este nevoie (i, prin
urmare, putem lucra i cu liste infinite!). Evaluarea ntrziat se poate
implementa prin reprezentarea unui term printr-un graf (i nu printr-un arbore).
Orice nod asociat mai multor arce reprezint un subterm de a crui valoare este
nevoie de mai multe ori. Ori de cte acel subterm este redus, rezultatul va
rescrie nodul, iar toate referirile la acel subtem vor avea acces imediat la
aceast nlocuire.
Exerciii
1. Efectuai substituiile (y.x(x.x))[(y.yx)/x] i (y(z.xz))[(y.zy)/x].
2. Reducei n dou moduri (x.xx)(Ia), unde I (x.x).
3. Ce se ntmpl cu reducerea lui (xy.L)MNdacy este liber n M
4. Reducei n dou moduri (x.(y.xy)z)y.
5. Reducei ((x.y.addxy)3)4.
6. Artai egalitatea (fgx.fx(gx))(xy.x)(xy.x) = x.x
-
8/9/2019 Programare Functionala. Haskell
30/206
30
4 Codificarea datelor in -calcul
Lambda calculul este suficient de expresiv pentru a permite codificarea
valorilor booleene, a perechilor ordonate, a numerelor naturale i a listelor -
adic toate structurile de date necesare ntr-un program funcional. Aceste
codificri permit (n mod virtual) s modelm ntreaga programare funcional
ntre limitele oferite de lambda calcul.
Codificrile ar putea, unele dintre ele, s par nenaturale, i nu sunt
eficiente din punct de vedere computaional. Prin aceasta ele se aseamn cu
codificrile i programele mainii Turing. Dar, spre deosebire de acestea,
codificrile din lambda calcul sunt interesante i din punct de vedere
matematic, ele aprnd iari iar n studiile teoretice. Multe din ele conin ideeac orice structur de date poate avea i structura de control n ea.
Valorile booleene
Orice codificare a valorilor booleene trebuie s defineasc termii true,
false, if, i s satisfac (pentru orice MiN):
if true MN= M
if false MN=N.
De obicei se utilizeaz urmatoarea codificare:
true xy.x
false xy.y
if pxy.pxy
Conform teoremei Church-Rosser, este clar c true false, deoarece ele
sunt forme normale distincte. Mai mult, if nu este nici mcar necesar s fie
-
8/9/2019 Programare Functionala. Haskell
31/206
31
definit, deoarece valorile de adevr au coninute n ele nsele operatorul
condiional:
true MN (xy.x)MN* M
false MN (xy.y)MN* N
Toate operaiile uzuale pe valorile de adevr se pot defini cu operator
condiional. Codificrile pentru conjuncie, disjunciei negare sunt:
and pq.ifp q false
or pq.ifp true q
not p.ifp false true
Perechi ordonate
Dup ce am definit truei false, definim urmtoarele funcii: pair care
este o funcie de construcie a perechilor i proieciile fst i snd. Ele sunt
codate n lambda calcul astfel:
pair xyf.fxy
fst p.p true
snd p.p false
Este clar ca pairMN (xyf.xyf)MNf.fMN, mpachetnd pe Mi
peNmpreun. Unei perechi i se poate aplica orice functie de 2 argumente, deforma xy.L, obinndu-seL[M/x][N/y].
Proieciile lucreazi ele dup acelai principiu; se poate verifica uor, i
se propune ca exerciiu, cfst (pair MN) * Mi snd (pair MN) *N.
-
8/9/2019 Programare Functionala. Haskell
32/206
32
Numerele naturale
Alonso Church a dezvoltat o codificare elegant a numerelor naturale n
lambda calcul, i, chiar dac dup aceasta au mai aprut i alte codificari,
codificarea sa se utilizeazi n lambda calculul de ordin doi.
Definim
0 fx.x
1 fx.fx
2 fx.f(fx)
...........
n fx.f(...(fx)...)
...........Vom numi aceti termi numeralelelui Church; aadar, numeralul n este
funcia care ducefnfn.
Codificarea operaiilor pe numerale
Pentru adunare, nmulire i ridicare la putere, vom folosi urmtoarele
codificri:
add mnfx.mf(nfx)
mult mnfx.m(nf)x
expt mnfx.nmfx
Aceste operaii nu sunt suficiente ns pentru definirea tuturor funciilor
calculabile pe numere naturale. Vom defini, n continuare, i ali termi pentru
funcia succesori testarea cu zero.
suc nfx.f(nfx)
-
8/9/2019 Programare Functionala. Haskell
33/206
33
iszero n.n(x.false) true
Se pot verifica uor urmtoarele reduceri, pentru orice numeral Church n:
sucn * n+1
iszero 0 * true
iszero (n+1) * false
Funcia predecesoriscderea sunt codificate prin
prefn fp.pair (f(fstp))(fstp)
pre nfx.snd (n(prefnf)(pairxx))
sub mn.nprem
Modalitatea de codificare a funciei predecesorpre pare greoaie dar este
datorat faptului c trebuie sa reducem un n +1 iterator la un n iterator. Adic,
datefix, trebuie s determinmgiy astfel nctgn+1y calculeazfnx. O astfel
de funcie este funcia care satisface g(x, z) = (f(x),x). Se observ imediat c
gn+1(x,x) = (fn+1(x),fn(x)).
Termul preffconstruiete, n fond, funcia g. Pentru scdere, sub m n
calculeaza al n-lea predecesor al lui m.
Exerciii.
1. Verificai cadd m n * m+n si ca mult m n * mn.
2. Verificai cpre (n+1) * ni pre (0) * 0.
Liste
Numeralele Church se pot generaliza pentru a putea reprezenta liste. O
lista de forma [x1, x2, ...,xn] poate fi reprezentat prin funcia care ducefiy
nfx1( fx2 ...(fxny) ... ). Astfel construite, listele i conin i structura de control.
-
8/9/2019 Programare Functionala. Haskell
34/206
34
Ca alternativa, listele se pot reprezenta cu ajutorul mperecherii. Aceast
codificare este mai uor de inteles ntrucat este mult mai apropiat de
implementarile reale.
Lista [x1, x2, ...,xn] se va reprezenta prinx1 :: x2 :: ... :: nil. Pentru a pstra
exprimrile ct mai simple, vom utiliza dou nivele de mperechere. Fiecare
celula consx ::y se poate reprezenta prin (false(x,y)), unde false este privit
ca un etichet distins. nil ar trebui reprezentat printr-o pereche a crei prima
component este true, cum ar fi (true,true), dar se poate utiliza i o definiie
mai simpl. Codificrile sunt:
nil z.z
cons xy.pair false (pairxy)
null fst
head z. fst (sndz)
tail z.snd(sndz)
Exerciii. Verificai urmtoarele proprieti:
1. null nil * true.
2. null(cons MN) * false.
3. head (cons MN) * M.
4. tail (cons MN) *N
S mai remarcm c, ultimele dou proprieti, propuse ca exerciiu, au
loc pentru orice termi MiNchiari n cazul n care ei nu au forme normale.
Din acest motiv, pairi cons sunt constructori lenei sau ntrziai adic
-
8/9/2019 Programare Functionala. Haskell
35/206
35
ei nu i evalueaz argumentele. Odat cu introducerea definiiilor recursive,
vom fi astfel capabili s lucrm cu liste infinite.
5 Scrierea funciilor recursive n lambda calcul
Recursia este esenial n programarea funcionala. Cu ajutorul
numeralilor lui Church este posibil s definim aproape-toate funciile
calculabile pe numere naturale. Numeralii lui Church au o surs intern de
repetiie. De aici, putem deriva recursia primitiv care, atunci cnd se aplic
utiliznd funcii de ordin nalt, definesc o clas mai larg dect cea a funciilor
recursive studiate in Teoria Calculabilitii. Chiar dacfuncia lui Ackermann
nu este primitiv recursiv n sens uzual, ea se poate codifica utiliznd numeralii
lui Church. Definind
ack m.m(fn.nf(f1)) suc,
putem obine ecuaiile recursive satisfcute de funcia lui Ackermann, adic
ack 0 n = n+1
ack(m+1) 0 = ack m 1
ack(m+1) (n+1) = ack m (ack(m+1) n)
S le verificm:
ack 0 n * 0(fn.nf(f1))suc n * suc n * n+1
Pentru celelalte dou ecuatii, s observm mai nti c:
ack(m+1) n * (m+1)(fn.nf(f1))suc n * (fn.nf(f1))(m(fn.nf(f1))suc)n
= (fn.nf(f1))(ack m )n * n (ack m) (ack m 1)
Particulariznd, obinem:
ack(m+1) 0 * 0(ack m)(ack m 1) * ack m 1, i
-
8/9/2019 Programare Functionala. Haskell
36/206
36
ack(m+1)(n+1) * n+1 (ack m)(ack m 1) * ack m (n (ack m)(ack m 1))
= ack m(ack(m+1) n)
Utilizarea punctelor fixe la functii recursive
Codificarea facut funciei lui Ackermann, chiar dac funcioneaz, pare
artificiali este nevoie de perspicacitate pentru a o obine. Probleme apar ns,
de exemplu, n cazul unei funcii ale crei chemri recursive implic ceva
mai mult dect o simpl scdere cu 1 a argumentului: astfel, mprirea se face
prin scderi succesive.
n lambda calcul se pot defini toate funciile recursive, chiari acelea a
cror definire poate fi interminabil pentru unu sau mai multe argumente.
Ideea de codificare a recursiei este uniform i este independent de
definiia recursiv i reprezentarea structurilor de date (spre deosebire de
modalitatea prezentat pentru funcia lui Ackermann, care utilizeaz numeralii
lui Church). Ea const n utilizarea aa numiilor combinatori de punct fix,
adic un - term W avnd proprietatea cWF=F(WF) pentru orice termF.
S detaliem puin terminologia utilizat. Un punct fix al unei funcii F
este orice Xcare satisface FX= X; n cazul nostru, X WF. Un combinator
este un - term care nu conine variabile libere. Pentru codificarea recursiei, F
reprezint corpul definiiei recursive; legea WF= F(WF) permite ca Fs fie
desfacut ori de cte ori este nevoie.
Utilizarea lui W
Vom codifica funcia factorial i lista infinit [0,0,0,...]. Aceste funcii
trebuie s satisfac ecuaiile recursive:
-
8/9/2019 Programare Functionala. Haskell
37/206
37
factN= if(iszeroN) 1 (multN(fact (preN)))
zeroes = cons 0 zeroes
Pentru aceasta, definim
fact W(gn. if( iszero n) 1 (mult n(g(pre n))))
zeroes W(g.cons 0g)
n fiecare dintre definiii, chemarea recursiv este inlocuit prin variabil
gn W(g....). S verificm recursia pentru zeroes:
zeroes W (g. cons 0g) = (g. cons 0g)(W (g. cons 0g)) =
(g. cons 0g) zeroes cons 0 zeroes
n general, ecuaia recursivM=PM, undePeste un term oarecare, este
satisfacut dac definim M WP. S considerm un caz particular, cazul n
care Meste o funcie de n variabile. Ecuaia M x1 x2 ... xn=PMeste verificat
dac definim
MW (gx1 x2 ...xn.Pg)
ntruct
Mx1 x2 ...xn W (gx1 x2 ...xn.Pg)x1 x2 ...xn
= (gx1 x2 ...xn.Pg) Mx1 x2 ...xn
PM
S considerm acum cazul definiiei recursive mutuale, pentru doi
termi MiN, avnd corpurileP, respectiv Q, adic
M=PMN
N= QMN
n acest caz, ideea de baz este de a considera punctul fix pentru o funcieFpe
perechi, astfel inct F(X,Y) = (PXY, QXY). Utiliznd codificarea dat
perechilor, definim
-
8/9/2019 Programare Functionala. Haskell
38/206
38
L W (z.pair (P(fstz)(sndz)) (Q(fstz)(sndz)))
M fstL
N sndL
Din proprietatea de punct fix,
L pair (P(fstL)(sndL)) (Q(fstL)(sndL))
i, aplicnd proieciile, obinem egalitile dorite,
M=P(fstL)(sndL) =PMN
N= Q(fstL)(sndL) = QMN
Exemple de combinatori de punct fix
Combinatorul W a fost descoperit de Haskell B.Curry. El este definit prin
W f.(x.f(xx))(x.f(xx))
S verificm faptul c el satisface proprietatea de punct fix:
WF (x.F(xx))(x.F(xx)) F((x.F(xx))(x.F(xx))) =F(WF)
Verificarea s-a efectuat prin utilizarea a dou-reduceri urmate de o
- expansiune. Nu exist nici o reducere de genul WF*F(WF).
Existi ali combinatori:
Combinatorul al lui A. Turing. El este definit prin AA, unde
A xy.y(xxy)
Combinatorul $ al lui Klop. El se defineste ca fiind
$ LLLLLLLLLLLLLLLLLLLLLL, iar
L abcdefghijklmnopqstuvwxyzr.r(thisisafixedpointcombinator)
-
8/9/2019 Programare Functionala. Haskell
39/206
39
Ajuni aici, ncheiem scurta introducere n lambda calcul i n modul prin
care orice structura de date se poate codifica n lambda calcul. Cititorul
interesat poate consulta bibliografia ([1],[6],[14]).
Merit totui, n final, s facem o scurt comparaie ntre lambda calcul i
mainile Turing.
-calculul poate codifica toate structurile comune de date, cum ar fi
valorile booleene i listele, astfel nct s fie satisfacute proprietile lor
naturale. El poate exprima, de asemenea, definiiile recursive. ntruct aceste
codificri sunt tehnice, ar putea prea ca nu merit studiate, nsa nu este cazul:
Codificarea via numeralii lui Church se utilizeaz n calcul mult mai
avansat, cum ar fi -calculul de ordin doi.
Codificarea listelor via perechi ordonate modeleaz implementarea lor
uzual pe computer
nelegerea definiiilor recursive ca puncte fixe este metoda uzual din
teoria semantic.
Aceste construcii i concepte se ntlnesc n toat informatica teoretic,
ceea ce nu se poate spune ns despre programele mainilor Turing.
-
8/9/2019 Programare Functionala. Haskell
40/206
40
-
8/9/2019 Programare Functionala. Haskell
41/206
41
Capitolul II
Limbajul Haskell 98 descriere general
1. Istoric
Limbajul Haskell nu se poate luda cu o vrst naintat. Din acestpunct de vedere, limbajul Lisp este cu mult mai vechi, i se bucura de mult mai
mult atenie n programele analitice a multor universiti.
Pe de alt parte, n septembrie 1987, a avut loc o conferin asupra
Functional Programming Languages and Computer Architecture (FPCA 87)
n SUA; n cadrul acestei conferine s-a hotrt c este momentul s se pun
bazele unui nou limbaj de programare funcional care s aib ct mai multe
dintre proprietile i avantajele ale unui limbaj funcional pur. Acest lucru era
necesar ntruct n acel moment existau peste dousprezece limbaje de
programare funcional, avnd, n mare, aceeai expresivitate i semantic. Era
clar c, n acel moment, datorit mprtierii, nu se puteau bucura de succes
n aplicarea lor pe scar larg.
Comitetul format n scopul menionat a pornit de la urmtoarele
caracteristici care trebuiau a fi satisfcute de limbaj, [2]:
-
8/9/2019 Programare Functionala. Haskell
42/206
42
1. Trebuie s fie potrivit pentru nvmnt, cercetare i aplicaii, inclusiv
pentru construcia unor sisteme mari.
2. Trebuie s poat fi descris complet prin publicarea sintaxei i semanticii
sale.
3. Trebuie s fie accesibil n mod liber. Oricine trebuie s aib voie s
implementeze limbajul i s-l distribuie oricui dorete.
4. Trebuie s se bazeze pe idei care se bucur de un larg consens.
5. Trebuie s reduc diversitatea (inutil) a limbajelor de programare
funcional.
Prima variant a limbajului, Haskell 1.0, a aprut n 1990. De atunci,
limbajul s-a dezvoltat continuu, ajungnd n 1997 la versiunea 1.4. n acel an,
la Haskell Workshop din Amsterdam, s-a hotrt c este momentul elaborriiprimei variante stabile, portabile, cu o bibliotec standard, i care s serveasc
drept baz pentru extinderile ulterioare. Aceast variant, numitHaskell 98, a
suferit mbuntiri continue din punct de vedere al bibliotecii standard
numit the Prelude, i asta datorit faptului c multe programe scrise n
Haskell au nevoie de o bibliotec de funcii mult mai larg. n momentul de
fa (2006) se afl n plin desfurare procesul de definire a succesorului
limbajului Haskell 98, numitHaskell(Haskell Prime), [4].
Urmtorul grafic, imaginat i realizat de Bernie Pope i Donald
Stewart, urmrete ndeaproape dezvoltarea, implementarea i utilizarea
limbajului Haskell, i poate fi gsit n "The History of Haskell", Paul Hudak,
John Hughes, Simon Peyton Jones, and Philip Wadler, the Third ACM
SIGPLAN History of Programming Languages Conference (HOPL III), San
Diego, ACM Press, June 2007.":
-
8/9/2019 Programare Functionala. Haskell
43/206
43
-
8/9/2019 Programare Functionala. Haskell
44/206
44
2. Proprieti caracteristice
Dintre metodele i instrumentele utilizate de limbajul Haskell amintim:
confruntarea abloanelor (pattern matching) saunu-nelege dar le
potrivete;
curry-zarea a se vedea n capitolul precedent modul de trecere de la
funcii de mai multe variabile la compuneri de funcii de o singur
variabil;
comprehensiunea listelor (list comprehension) procedeu de
construcie pentru procesarea listelor, asemntor cu definirea
mulimilor prin proprieti caracteristice ale elementelor;
grzi (guards) utilizate pentru specificarea diferit a corpului
funciilor n funcie de anumite condiii de ndeplinit;
recursivitatea (am discutat despre acest concept n capitolul
anterior);
evaluarea ntrziat(lazy evaluation) i despre acest lucru am
discutat n capitolul precedent;
Spre deosebire de alte limbaje, Haskell a introdus n programare utiliza-
rea unor concepte noi: monade ele sunt inspirate din teoria categoriilor (domeniu al algebrei
abstracte) i sunt utilizate, n principal, pentru a impune n cadrul unui
program funcional executarea unor operaii ntr-o ordine specificat;
vom reveni n capitolele urmtoare asupra lor.
clas de tipuri (tipul class) este o construcie a sistemului de tipuri
din Haskell prin specificarea operaiilor care trebuie i pot fi imple-
-
8/9/2019 Programare Functionala. Haskell
45/206
45
mentate fiecrui tip din cadrul unei clase. Atenie: aceast noiune este
diferit de noiunea de clasdin programarea orientat obiect.
Avnd aceste caracteristici i metode, limbajul Haskell realizeaz foarte
uor implementarea funciilor, lucru care nu se poate afirma despre limbajele
de programare procedurale. nainte de orice, s revenim i s mai punem n
eviden unele proprieti ale acestui limbaj, care nu se regsesc la cele
imperative:
Haskell este un limbaj lene sau ntrziat(lazy language), adicun program scris n Haskell nu face nici un calcul dect atunci cnd este forat
de necesitatea utilizrii acelei evaluri ntr-un anumit calcul. Acest lucru nu
este numai o optimizare a calculului ci, mai degrab, un mod extrem de
puternic de lucru. Liniile de cod care, altfel, sunt bucle infinite sau consumlargi poriuni de memorie devin instrumente foarte simple de utilizat n
Haskell, i asta deoarece nu existfor sau while n limbaj
Haskell face o distincie clar ntre valori (numere 1,2,3,...; iruri:
abc, salut, ...; caractere: a,b,...; chiar i funcii: funcia de ridicare la
ptrat, sau funcia radical) i tipuri (categoriile din care care fac parte valorile)
(case sensitive). Aceasta nu este o caracteristic numai a Haskell ului,
ntruct marea majoritate a limbajelor au un anumit sistem de tipuri. Ceea ce
este specific ns este faptul c numele date funciilor i valorilor ncep cu
liter mic i numele dat tipurilor ncep cu liter mare. Atenie: dac un
anumit program, de altfel corect, nu poate fi compilat i d erori, trebuie
verificat dac nu cumva s-a denumit o funcie cu liter mare.
Haskell nu are efecte secundare. Un efect secundar este ceea ce se
ntmpl n cursul execuiei unei funcii, care nu este legat de ieirea funciei.
De exemplu, ntr-un limbaj cum ar fi C sau Java, avem voie s modificm
-
8/9/2019 Programare Functionala. Haskell
46/206
46
variabilele globale dintr-o funcie. Acest lucru este un efect secundar, ntruct
modificrile aduse acestei variabile globale nu sunt legate de ieirea produs
de funcie. Mai mult, modificarea starii lumii reale este considerat efect
secundar: apariia unor lucruri pe ecran, citirea unui fiier, etc., toate sunt
operaii cu efecte secundare. Funciile care nu au efecte secundare se numesc
funcii pure. Pentru a vedea dac o anumit funcie este pur sau nu este
trebuie studiat dac: Date aceleai argumente, funcia va produce tot timpul
aceleai ieiri?
Acesta este unul dintre motivele pentru care muli programatori n C sau
Java au probleme n nelegerea modelului funcional de programare. Din
acest punct de vedere, valorile trebuie gndite n mod diferit. De exemplu, o
valoare x nu trebuie gndit ca un registru, o locaie de memorie sau oricealtceva de aceast natur. x este, simplu, un nume, dupa cum Haskell este
numele limbajului pe care dorim s-l studiem. Nu se poate decide, n mod
arbitrar, s se stocheze un limbaj diferit n numele dat. Asta nseamn c
urmtoarele linii de cod n C nu au contrapartid n Haskell:
Int x = 5;
x = x+1;
O chemare de gen x = x + 1 este numit rennoire distructiv
(destructive update), ntruct se distruge orice era n x nainte i se nlocuiete
cu noua valoare. Aceste rennoiri nu exist n Haskell. n schimb, o variabil
Haskell spune compilatorului ceva de genul: dac vreodat ai nevoie stii
valoarea lui x, uite cum o poi calcula.
Codul scris n Haskell este uor de neles ntruct nu admite rennoiri
distructive. Adic dac vom defini o funcie fi chemm acea funcie cu un
anumit argument a la nceputul programului i, la sfritul programului
-
8/9/2019 Programare Functionala. Haskell
47/206
47
chemm din nou f cu acelai argument a vom ti c vom obine acelai rezultat.
Acest lucru se ntmpl deoarece tim ca nu putea s se schimbe i cf
depinde numai de a. Aceast proprietate care, n esen, afirm c, dac dou
funcii fi g produc aceleai valori pentru aceleai argumente, putem nlocui f
cu g (i vice-versa) se numete transparenreferenial.
Haskell se poate interpreta sau compila foarte uor n cod main. De
asemenea, permite realizarea unei interfee cu limbajul C; de exemplu, se pot
apela uor funiile scrise n C i asta n numai 2-3 linii de cod. Exist, de ase-
menea, interfee i cu Java, .NET sau Python
Scrierea codului n Haskell este, n mod surprinztor, foarte intuitiv,
iar citirea i nelegerea codului scris sunt, de asemenea, foarte uoare. Din
acest motiv, este mai puin nevoie de un debugger pentru Haskell.Haskell este un limbaj de programare didactic. n acest sens, este
mai uor s scriem programe n Haskell dect n (de exemplu) C++, i asta din
cauz c scrierea celui mai simplu program n C++ necesit cunotine despre
biblioteci, I/O i alte detalii sintactice; odat scrise ele trebuie compilate i
apoi rulate nainte de a fi testate. Pentru Haskell, interpretoarele GHCi sau
Hugs lucreaz mai mult ca nite calculatoare de buzunar. Modul de execuie al
Haskell-ului este bazat pe evaluarea expresiilori este mai uor de neles.
Pentru a nelege programele C++ trebuie s se neleag mai nti modelul
main. Mai mult, majoritatea algoritmilor se scriu n Haskell aproape n
acelai limbaj ca acela n care descriem algoritmul. Astfel, pentru
nceptorii n programare este mai uor s nceap cu Haskell. Din acest punct
de vedere, Haskell este de nivel mai nalt dect C++, fiind orientat mai mult
ctre utilizator dect ctre main.
-
8/9/2019 Programare Functionala. Haskell
48/206
48
Sperm c toate proprietile i avantajele enunate mai sus constituie
tot attea motive pentru a motiva citirea, n continuare, a acestei cri.
ns, pentru aceasta, vom avea nevoie i de:
3. Implementri i instalare.
Marea majoritate a implementrilor limbajului Haskell conin un numr
suficient de mare de biblioteci care s suporte construciile i metodele
prezentate n paragraful anterior. Informaii privind aceste implementri se pot
gsi n bibliografia existent la sfritul acestui capitol, iar o list a lor se
poate consulta deja n graficul prezentat la sfritul paragrafului dedicatistoricului acestui limbaj de programare.
Implementarea realizat la Universitatea din Glasgow, Glasgow Haskell
Compiler (GHC), este, dup prerea noastr, cea mai bun, avnd, simultan,
pe lng compilator i interpretor, cele mai complete biblioteci, cea mai
complet documentaie i cea mai larg rspndire. De asemenea, exist
variante pentru marea majoritate a sistemelor de operare i a platformelor
existente la aceast or.
GHC ul compileaz programele Haskell sau n cod nativ sau n C.
Totodat, el implementeazi multe dintre extensiile experimentale ale Haskell
98. El vine, de asemenea, cu un garbage collector automat.
Se poate obine liber, de pe pagina web http://haskell.org/ghc/ de unde
se poate descrca ultima versiune potrivit platformei pe care se lucreaz.
Exist distribuii binare pentru aproape orice sistem de operare, iarinstalarea
se realizeaz urmnd aceeai pai ca n cazul oricrui alt program.
-
8/9/2019 Programare Functionala. Haskell
49/206
49
n ceea ce urmeaz ne vom referi numai la distribuia dedicat sistemu-
lui de operare Windows i asta din cauza faptului c este cel mai rspndit
sistem de operare din ara noastr. n orice caz, codul care se va scrie de
utilizator nu este att de legat de sistemul de operare, putnd fi exportat uor
ctre alte sisteme.
Rularea compilatorului:
S presupunem c avem un program scris n Haskell, numit Test.hs
care conine o funcie test. Compilarea acestui program se face prin scrierea
urmtoarei linii n dreptul prompter-ului:
% ghc --make Test.hs -o test.exe
Opiunea -make spune compilatorului c avem de compilat un
program (i nu avem de a face cu o simpl bibliotec) i c trebuie s constru-im i toate modulele de care depinde programul. Test.hs este numele
fiierului de compilat iar -o test.exe spune compilatorului unde se pune
rezultatul compilrii, adic n fiierul executabil test.exe.
Rularea interpretorului:
Vom utiliza, pentru testarea rapid a codului scris, interpretorul GHCi.
El se invoc ori prin comanda ghci ori prin selectarea iconiei din
Start/Programs/GHC/.... Odat pornit, este ncrcat modulul principal, Prelude,
care conine toate tipurile, operaiile i modulele de baz. Dnd comanda
Prelude> :?
se obine un help imediat.
S mai observm c una dintre opiunile cele mai importante pe care
utilizatorul le poate lua n acest moment este de a deschide toate extensiile,
lucru realizabil prin comanda:
Prelude> :set fglasgow-exts
-
8/9/2019 Programare Functionala. Haskell
50/206
50
Acest lucru este indicat ori de cte ori vom dori s rulm un cod scris de
altcineva, i cu care avem probleme de ncrcare.
4. Scrierea codului surs
Pentru scrierea codului este nevoie de un editor de texte. Preferabil ar
fi unul pentru care exist posibilitatea evidenierii sintaxei. Pentru Windows,
UltraEdit sau TextPad realizeaz acest lucru, ns ambele sunt soft proprietar.
La fel, utilizarea lui Visual Haskell (chiar dac nu are nevoie de editoare de
text) presupune existena n sistem a Visual Studio .NET 2003 sau 2005. n
orice caz, se poate utiliza fr probleme Notepad-ul de sub Windows, fr a
avea ns evidenierea sintaxei.
Tipuri de fiiere:
Exist dou tipuri de fiiere Haskell, *.hs sau *.lhs.
Fiierele de tipul *.lhs (literate Haskell script) sunt bazate pe un stil
alternativ de scriere a codului surs n limbajul Haskell, stil care ncurajeaz
comentariile, ele fiind considerate ca fiind iniiale. n acest sens, numai
liniile care ncep cu > sunt tratate ca fcnd parte din program, toate
celelalte linii sunt comentarii. n felul acesta, utilizatorul este ncurajat scomenteze, fcnd codul mai uor de neles. Utiliznd acest stil, un program
care cere utilizatorului un numri afieaz factorialul su ar aprea n modul
urmtor:
This literate program prompts the user for a
number and prints the factorial of that number:
-
8/9/2019 Programare Functionala. Haskell
51/206
51
> main :: IO ()
> main = do putStr "Enter a number: "
> l putStr "n!= "
> print (fact (read l))
This is the factorial function.
> fact :: Integer -> Integer
> fact 0 = 1
> fact n = n * fact (n-1)
Fiierele *.hs (Haskell script) apar, mai simplu, fr a avea > la
nceputul liniei de cod; n schimb comentariile apar n unul din urmtoarele
moduri:
comentarii de linie: ele ncep cu -- i se extind pn la sfritul liniei
comentarii de bloc: ele ncep cu {- i se extind pn la nchidere, prin
-}. Comentariile de bloc pot conine, n interior, alte comentarii de
bloc.
Comentariile se utilizeaz pentru a explica programul (n englez) i
sunt ignorate de compilatoare i interpretoare. De exemplu:
module Test2
where
main =
putStrLn "Hello World"
-
8/9/2019 Programare Functionala. Haskell
52/206
52
-- write a string
-- to the screen
{- f is a function which takes an integer and
produces an integer. {- this is an embedded
comment -} the original comment extends to thematching end-comment token: -}
f x =
case x of
0 -> 1 -- 0 maps to 1
1 -> 5 -- 1 maps to 5
2 -> 2 -- 2 maps to 2
_ -> -1 -- everything else maps to -1
Acest exemplu utilizeaz ambele tipuri de comentrii.Ca i reguli de baz n realizarea fiierelor surs:
comentai liniile de program nc din faza de design i meninei-le i n
faza de dezvoltare;
includei un comentariu de nceput n care s specificai numele
dumneavoastr, data, scopul realizrii programului;
ncercai s includei, de fiecare dat, declaraia de tip a valorilor,
expresiilor, funciilor i a altor tipuri de date utilizate (chiar dacHaskell realizeaz acest lucru automat n majoritatea cazurilor), codul
fiind mai uor de neles de ali utilizatori;
Important. nainte de a defini noi nine funcii, operatori .a.m.d.
trebuie, ntotdeauna, s aruncm o privire peste funciile predefinite n
Prelude.hs.
-
8/9/2019 Programare Functionala. Haskell
53/206
53
Vom reveni asupra fiierelor surs, ns menionm de la nceput c am
ales s utilizm n aceast carte cea de a doua modalitate de scriere a codului
surs (adic Haskell script i nu literate Haskell script).
Not: n tot ceea ce urmeaz, liniile de cod vor fi scrise n fontul Courier
cu caractere nengroatei, ori de cte ori ne vom referi la interac-
iunea noastr cu sistemul de operare sau cu programul utilizat, vom scrie, tot
n fontul Courier cu caracterele ngroate.
5. Un tur rapid al Prelude - ului
Una din greelile comune care se fac, atunci cnd utilizatorul i
definete propriile funcii ntr-un program, este c se utilizeaz acelai nume
cu numele unor funcii predefinite n fiierul Prelude.hs. Din acest motiv
am decis ca, n ncheierea acestui capitol, s prezentm funciile predefinite
din Haskell n Prelude. Aceast seciune poate fi lsat la o parte la o prim
lectur, urmnd a ne ntoarce ori de cte ori este nevoie.
Aadar, ori de cte ori vom dori s definim o funcie cu numele fooi
nu suntem siguri dac ea nu este cumva predefinit n Prelude sau n modulele
care sunt importate de programul pe care l concepem, este bine s atamparticula my, adic vom numi funcia prin my_foo.
Funciile din Prelude:
abs
tipul: abs :: Num a => a -> a
descriere: returns the absolute value of a number.
-
8/9/2019 Programare Functionala. Haskell
54/206
54
definiie: abs x
| x >= 0 = x
| otherwise = -x
utilizare: Prelude> abs (-5)
5
all
tipul: all :: (a -> Bool) -> [a] -> Bool
descriere: aplicat unui predicat i o list, rezultatul este True
dac toate elementele listei satisfac acel predicat, i este
False n caz contrar. Este asemntoare funciei any.
definiie: all p xs = and (map p xs)utilizare: Prelude> all ( all isDigit "1a2b3c"
False
and
tipul: and :: [Bool] -> Bool
descriere: funcia face conjuncia unei liste de valori booleene.
definiie: and xs = foldr (&&) True xs
utilizare: Prelude> and [True, False, True, True]
False
Prelude> and [True, True, True, True]
True
Prelude> and []
True
-
8/9/2019 Programare Functionala. Haskell
55/206
55
any
tipul: any :: (a -> Bool) -> [a] -> Bool
descriere: aplicat unui predicat i o list, rezultatul este True dac exist
elemente n list care satisfac predicatul, i False n caz contrar.
definitie: any p xs = or (map p xs)
utilizare: Prelude> any ( any isDigit "1a2b3c"
True
Prelude> any isDigit "alphabet"
False
atan
tipul: atan :: Floating a => a -> a
descriere: funcia trigonometric tangent.
definiie: definit intern.
utilizare: Prelude> atan (pi/3)
0.808448792630022
break
tipul: break :: (a -> Bool) -> [a] -> ([a],[a])
descriere: dat un predicat i o list, funcia mparte lista n dou liste aflate
ntr-o pereche, n primul punct unde este satisfcut predicatul. n
cazul n care predicatul nu este satisfcut n nici un punct,
-
8/9/2019 Programare Functionala. Haskell
56/206
56
perechea rezultat are primul element ntreaga list iniial iar al
doilea element este lista vid, [].
definiie: break p xs
= span p' xs
where
p' x = not (p x)
utilizare: Prelude> break isSpace "salut cititorule"
("salut", "cititorule")
Prelude> break isDigit "lista are cifre ?"
("lista are cifre ?","")
ceiling
tipul: ceiling :: (RealFrac a, Integral b) => a -> b
descriere: aplicat unui numr real ea ne ofer cel mai mic numr ntreg
mai mare sau egal cu numrul dat.
utilizare: Prelude> ceiling 2.54
3
Prelude> ceiling (- pi)
-3
chr
tipul: chr :: Int -> Char
descriere: aplicat unui numr ntreg ntre 0 i 255, ni se ofer
caracterul al crui codificare ASCII este acel. Aceast funcie
are drept invers funcia ord. Aplicat unui numr ntreg n
-
8/9/2019 Programare Functionala. Haskell
57/206
57
afara domeniului dat mai nainte, se va obine un mesaj de
eroare.
definiie: definit n mod intern.
utilizare: Prelude> chr 65
'A'
Prelude> (ord (chr 65)) == 65
True
concat
tipul: concat :: [[a]] -> [a]
descriere: aplicat unei liste de liste, funcia le unete utiliznd operatorul
de concatenare ++definiie: concat xs = foldr (++) [] xs
utilizare: Prelude> concat [[4],[1,2,3],[],[5,6,7]]
[4,1,2,3,5,6,7]
cos
tipul: cos :: Floating a => a -> a
descriere: funcia trigonometric cosinus; argumentele sunt interpretate cafiind n radiani.
definiie: definit intern.
utilizare: Prelude> cos pi
-1.0
Prelude> cos (pi/2)
6.123031769111886e-17
-
8/9/2019 Programare Functionala. Haskell
58/206
58
digitToInt
tipul: digitToInt :: Char -> Int
descriere: aceast funcie face o conversie: transform o cifr privit ca i
caracter n valoarea corespunztoare
definiie: digitToInt :: Char -> Int
digitToInt c
| isDigit c = fromEnum c - fromEnum '0';
| c >= 'a' && c = 'A' && c digitToInt '3'
3
div
tipul: div :: Integral a => a -> a -> a
descriere: aceast funcie calculeaz ctul mpririi dintre argumentele
sale.definitie: definit intern.
utilizare: Prelude> 24 `div` 9
2
doReadFile
tipul: doReadFile :: String -> String
-
8/9/2019 Programare Functionala. Haskell
59/206
59
descriere: aceast funcie citete fiiere: dat numele fiierului ca un string
ea ofer coninutul fiierului sub forma unui string. n cazul n
care fiierul nu poate fi gsit sau nu poate fi deschis se va afia
un mesaj de eroare.
definiie: definit intern.
utilizare: Prelude> doReadFile "foo.txt"
"This is a small text file,\ncalled
foo.txt.\n".
drop
tipul: drop :: Int -> [a] -> [a]
descriere: se aplic unui numri unei liste. Se va obine o nou list din
care sunt terse attea caractere ct este valoarea numrului. n
cazul n care lista are mai puine elemente dect este numrul, se
obine lista vid.
definiie: drop 0 xs = xs
drop _ [] = []
drop n (_:xs) | n>0 = drop (n-1) xs
drop _ _ = error "PreludeList.drop:
negative argument"
utilizare: Prelude> drop 5 [1..10]
[6, 7, 8, 9, 10]
Prelude> drop 4 "abc"
""
dropWhile
-
8/9/2019 Programare Functionala. Haskell
60/206
60
tipul: dropWhile :: (a -> Bool) -> [a] -> [a]
descriere: aplicat unui predicat i o list, ea scoate toate elementele din
capul listei care stisfac acel predicat.
definiie: dropWhile p [] = []
dropWhile p (x:xs)
| p x = dropWhile p xs
| otherwise = (x:xs)
utilizare: Prelude> dropWhile ( a -> [a] -> Booldescriere: se aplic unei valori i unei liste iar ca ieire obinem
True dac valoarea este n listi False n rest. Elementele din
list trebuie s fie de acelai tip ca i valoarea din argument.
definitie: elem x xs = any (== x) xs
utilizare: Prelude> elem 5 [1..10]
True
Prelude> elem "rat" ["fat","cat","sat"]
False
error
tipul: error :: String -> a
descriere: aplicat unui string creaz valoare de eroare cu un mesaj
asociat. Valorile de eroare sunt echivalente valorii nedefinite
-
8/9/2019 Programare Functionala. Haskell
61/206
61
(undefined); orice ncercare de a accasa valoarea, face ca
programul s se opreasc n acel punct i s apar un string ca i
diagnostic.
definiie: definit intern.
utilizare: error "this is an error message"
exp
tipul: exp :: Floating a => a -> a
descriere: funcia exponenial (exp n este echivalent cu en).
definitie: definit intern.
utilizare: Prelude> exp 1
2.718281828459045
filter
tipul: filter :: (a -> Bool) -> [a] -> [a]
descriere: aplicat unui predicat i o list, ea ne ofer ca rezultat o list
care conine toate elementele care satisfac acel predicat.
definiie: filter p xs = [k | k filter isDigit "1.sunt10motani"
"110"
flip
tipul: flip :: (a -> b -> c) -> b -> a -> c
-
8/9/2019 Programare Functionala. Haskell
62/206
62
descriere: apcat unei funcii de dou variabile, ea ne ofer aceeai funcie
avnd argumentele inversate.
definiie: flip f x y = f y x
utilizare: Prelude> flip elem [1..5] 5
True
Prelude> flip div 9 24
2
floor
tipul: floor :: (RealFrac a, Integral b) => a -> b
descriere: aplicat unui numr, ea calculeaz cel mai mare numr ntregcare nu depete numrul (partea ntreag a unui numr).
utilizare: Prelude> floor 3.6
3
Prelude> floor (-2.8)
-3
foldl
tipul: foldl :: (a -> b -> a) -> a -> [b] -> a
descriere: mpacheteaz o list utiliznd un operator binar dat i o
valoare dat de pornire, n manier asociativ stng.
foldl op r [a, b, c] ((r `op` a) `op` b) `op`c)
definitie: foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
utilizare: Prelude> foldl (+) 1 [1..10]
-
8/9/2019 Programare Functionala. Haskell
63/206
63
56
Prelude> foldl (flip (:)) [] [1..5]
[5, 4, 3, 2, 1]
Prelude> foldl (/) 2 [2,2,5]
0.1
foldl1
tipul: foldl1 :: (a -> a -> a) -> [a] -> a
descriere: mpacheteaz la stnga listele nevide.
definitie: foldl1 f (x:xs) = foldl f x xs
utilizare: Prelude> foldl1 max [1, 1, 5, 2, -1]
5
foldr
tipul: foldr :: (a -> b -> b) -> b -> [a] -> b
descriere: mpacheteaz o list utiliznd un operator binar dat i o
valoare dat de pornire, n manier asociativ la dreapta.
foldr op r [a, b, c] a `op` (b `op` (c `op`r))
definiie: foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
utilizare: Prelude> foldr (++) [] ["ala","tur","are"]
"alaturare"
Prelude> foldr (/) 2 [2,2,5]
2.5
Prelude> foldr (/) 2 [2,5,2]
0.4
-
8/9/2019 Programare Functionala. Haskell
64/206
64
foldr1
tipul: foldr1 :: (a -> a -> a) -> [a] -> a
descriere: mpacheteaz la dreapta listele nevide.
definiie: foldr1 f [x] = x
foldr1 f (x:xs) = f x (foldr1 f xs)
utilizare: Prelude> foldr1 (*) [1..5]
120
fromInt
tipul: fromInt :: Num a => Int -> a
descriere: face conversia de la un Int la un tip numeric aflat n clasa Num
utilizare: Prelude> (fromInt 5)::Float
5.0
fromInteger
tipul: fromInteger :: Num a => Integer -> a
descriere: face conversia de la un Integer la un tip numeric aflat n
clasa Num.
utilizare: Prelude> (fromInteger 100000000000)::Float1.0e11
fst
tipul: fst :: (a, b) -> a
descriere: extrage primul element al unei perechi.
definitie: fst (x, _) = x
-
8/9/2019 Programare Functionala. Haskell
65/206
65
utilizare: Prelude> fst (45, supa)
45
head
tipul: head :: [a] -> a
descriere: scoate primul element al unei liste. Cnd se aplic unei liste vide
se obine eroare.
definitie: head (x:_) = x
utilizare: Prelude> head [1..100]
1
Prelude> head ["mere", "pere", "caise"]
"mere"Prelude> head []
*** Exception: Prelude.head: empty list
id
tipul: id :: a -> a
descriere: funcia identitate.
definitie: id x = x
utilizare: Prelude> id 25
25
Prelude> id (id "mar")
"mar"
Prelude> (map id [1..10]) == [1..10]
True
-
8/9/2019 Programare Functionala. Haskell
66/206
66
init
tipul: init :: [a] -> [a]
descriere: aplicat unei liste, ea ne returneaz o lista care are toate
elementele celei iniiale cu excepia ultimului element; lista
iniial trebuie s fie nevid, altfel se obine o eroare.
definitie: init [x] = []
init (x:xs) = x : init xs
utilizare: Prelude> init [1..8]
[1, 2, 3, 4, 5, 6, 7]
Prelude> init studenti
student
isAlpha
tipul: isAlpha :: Char -> Bool
descriere: aplicat unui argument de tip Char, se obine True dac acel
caracter este liter din alfabet i False n rest.
definiie: isAlpha c = isUpper c || isLower c
utilizare: Prelude> isAlpha 'd'
True
Prelude> isAlpha '%'
False
isDigit
tipul: isDigit :: Char -> Bool
descriere: aplicat unui argument de tip Char, se obine True dac acel
caracter este cifri False n rest.
-
8/9/2019 Programare Functionala. Haskell
67/206
67
definiie: isDigit c = c >= '0' && c isDigit '1'
True
Prelude> isDigit 'a'
False
isLower
tipul: isLower :: Char -> Bool
descriere: aplicat unui argument de tip Char, se obine True dac acel
caracter este liter mic din alfabet i False n rest.
definiie: isLower c = c >= 'a' && c isLower 'a'True
Prelude> isLower 'A'
False
Prelude> isLower '1'
False
isSpace
tipul: isSpace :: Char -> Bool
descriere: aplicat unui argument de tip Char, se obine True dac acel
caracter este spaiu i False n rest.
definiie: isSpace c = c == ' '||c == '\t'||
c == '\n'||c == '\r' ||
c == '\f' || c == '\v'
utilizare: Prelude> dropWhile isSpace " \nhello \n"
"hello \n"
-
8/9/2019 Programare Functionala. Haskell
68/206
68
isUpper
tipul: isUpper :: Char -> Bool
descriere: aplicat unui argument de tip Char, se obine True dac acelcaracter este liter mare din alfabet i False n rest.
definitie: isDigit c = c >= 'A' && c isUpper 'A'
True
Prelude> isUpper 'a'
False
Prelude> isUpper '1'
False
iterate
tipul: iterate :: (a -> a) -> a -> [a]
descriere: iterate f x ofer lista infinit [x,f(x),f(f(x)), ...].
definiie: iterate f x = x : iterate f (f x)
utilizare: Prelude> iterate (+1) 1
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, .....
last
tipul: last :: [a] -> a
descriere: aplicat unei liste nevide, ea returneaz ultimul element din
list.
definiie: last [x] = x
last (_:xs) = last xs
-
8/9/2019 Programare Functionala. Haskell
69/206
69
utilizare: Prelude> last [1..10]
10
length
tipul: length :: [a] -> Int
descriere: ofer numrul de elemente dintr-o list.
definiie: length [] = 0
length (x:xs) = 1 + length xs
utilizare: Prelude> length [1..10]
10
linestipul: lines :: String -> [String]
descriere: aplicat unei liste de caractere care conin marcaje de linie nou,
\n, ea ofer o list de liste, prin desfacerea listei originale n
liste utiliznd marcajele drept delimitri. Marcajele sunt scoase
din lista rezultat.
definiie: lines [] = []
lines (x:xs)= l : ls
where
(l, xs') = break (== '\n') (x:xs)
ls
| xs' == [] = []
| otherwise = lines (tail xs')
utilizare: Prelude> lines "hello\nit's me,\neric\n"
["hello", "it's me,", "eric"]
-
8/9/2019 Programare Functionala. Haskell
70/206
70
log
tipul: log :: Floating a => a -> a
descriere: ofer logaritmul natural al argumentului.definiie: definit intern.
utilizare: Prelude> log 1
0.0
Prelude> log 3.2
1.1631508098056809
Prelude> log (exp 4)
4.0
map
tipul: map :: (a -> b) -> [a] -> [b]
descriere: aplicat unei funcii, i unei liste de orice tip, ea ofer o list n
care orice element este rezultatul aplicrii funciei elementului
corespunztor din lista iniial.
definiie: map f xs = [f x | x map sine [0,pi,pi/2]
[0.0,-1.2246063538223773e-16,1.0]
max
tipul: max :: Ord a => a -> a -> a
descriere: aplicat n dou valori de acelai tip, pentru care avem definit o
relaie de ordine, ea returneaz maximul dintre cele dou
elemente conform operatorului >=.
-
8/9/2019 Programare Functionala. Haskell
71/206
71
definiie: max x y
| x >= y = x
| otherwise = y
utilizare: Prelude> max 3 2
3
maximum
tipul: maximum :: Ord a => [a] -> a
descriere: aplicat unei liste nevide pentru ale crei elemente avem
definit o relaie de ordine, ea ne ofer elementul maxim din
list.definiie: maximum xs = foldl1 max xs
utilizare: Prelude> maximum [-10, 0 , 5, 22, 13]
22
min
tipul: min :: Ord a => a -> a -> a
descriere: aplicat n dou valori de acelai tip, pentru care avem definit orelaie de ordine, ea returneaz minimul dintre cele dou
elemente conform operatorului =
-
8/9/2019 Programare Functionala. Haskell
72/206
72
minimum
tipul: minimum :: Ord a => [a] -> a
descriere: aplicat unei liste nevide pentru ale crei elemente avemdefinit o relaie de ordine, ea ne ofer elementul minim din
list.
definiie: minimum xs = foldl1 min xs
utilizare: Prelude> minimum [-10, 0 , 5, 22, 13]
-10
mod
tipul: mod :: Integral a => a -> a -> a
descriere: aplicat n dou argumente, se obine restul modulo cel de-al
doilea argument a primului.
definiie: definit intern.
utilizare: Prelude> 15 `mod` 6
3
not
tipul: not :: Bool -> Bool
descriere: returneaz negaia logic a argumentului su boolean.
definiie: not True = False
not False = True
utilizare: Prelude> not (3 == 4)
True
-
8/9/2019 Programare Functionala. Haskell
73/206
73
Prelude> not (10 > 2)
False
or
tipul: or :: [Bool] -> Bool
descriere: aplicat unei liste de valori booleene, ea returneaz disjuncia
lor logic.
definiie: or xs = foldr (||) False xs
utilizare: Prelude> or [False, False, True, False]
True
Prelude> or [False, False, False, False]
FalsePrelude> or []
False
ord
tipul: ord :: Char -> Int
descriere: aplicat unui caracter, ea returneaz codul su ASCII ca ntreg.
definiie: definit intern.
utilizare: Prelude> ord 'A'
65
Prelude> (chr (ord 'A')) == 'A'
True
pi
tipul: pi :: Floating a => a
-
8/9/2019 Programare Functionala. Haskell
74/206
74
descriere: numrul , adic raportul dintre circumferina unui cerc i
diametrul su.
definiie: definit intern.
utilizare: Prelude> pi
3.14159
Prelude> cos pi
-1.0
putStr
tipul: putStr :: String -> IO ()
descriere: ia ca argument un string i returneaz o aciune I/O . Efectul
secundar al aplicrii putStr este c face ca string-ul arguments apar pe ecran.
definiie: definit intern.
utilizare: Prelude> putStr "Hello World\nI'm here!"
Hello World
I'm here!
product
tipul: product :: Num a => [a] -> a
descriere: aplicat unei liste de numere, ea returneaz produsul lor.
definiie: product xs = foldl (*) 1 xs
utilizare: Prelude> product [1..5]
120
repeat
-
8/9/2019 Programare Functionala. Haskell
75/206
75
tipul: repeat :: a -> [a]
descriere: dat o valoare, ea returneaz o list infinit de elemente cu
aceeai valoare.
definiie: repeat x
= xs
where xs = x:xs
utilizare: Prelude> repeat 12
[12, 12, 12, 12, 12, 12, 12, 12, ....
replicate
tipul: replicate :: Int -> a -> [a]
descriere: dat un numr natural i o valoare, ea returneaz o listconinnd numrul de instane al acelei valori.
definiie: replicate n x = take n (repeat x)
utilizare: Prelude> replicate 3 "apples"
["apples", "apples", "apples"]
reverse
tipul: reverse :: [a] -> [a]descriere: aplicat unei liste de orice tip, ea returneaz lista acelor
elemente, ns n ordine invers.
definiie: reverse = foldl (flip (:)) []
utilizare: Prelude> reverse [1..10]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
round
-
8/9/2019 Programare Functionala. Haskell
76/206
76
tipul: round :: (RealFrac a, Integral b) => a -> b
descriere: rotunjete argumentul la cel mai apropiat numr ntreg.
utilizare: Prelude> round 3.2
3
Prelude> round 3.5
4
Prelude> round (-3.2)
-3
show
tipul: show :: Show a => a -> String
descriere: convertete o valoare (membr
a clasei
Show),
la reprezentarea sa ca string.
definiie: definit intern.
utilizare: Prelude>"six plus two equals"++(show (6+2))
"six plus two equals 8"
sine
tipul: sine :: Floating a => a -> a
descriere: funcia trigonometric sinus, argumentele fiind interpretate n
radiani.
definiie: definit intern.
utilizare: Prelude> sin (pi/2)
1.0
Prelude> ((sin pi)^2) + ((cos pi)^2)
1.0
-
8/9/2019 Programare Functionala. Haskell
77/206
77
snd
tipul: snd :: (a, b) -> b
descriere: returneaz al doilea element dintr-o pereche.
definiie: snd (_, y) = y
utilizare: Prelude> snd ("harry", 3)
3
sort
tipul: sort :: Ord a => [a] -> [a]
descriere: sorteaz lista pe care o are drept argument o list n oredine
cresctoare. Elementele listei trebuie s fie n clasa Ord.
utilizare: List> sort [1, 4, -2, 8, 11, 0]
[-2,0,1,4,8,11]
observaie: Aceast funcie nuj este definit n Prelude. Trebuie importat
modulul List.hs.
span
tipul: span :: (a -> Bool) -> [a] -> ([a],[a])
descriere: dat un predicat i o list, funcia ofer o pereche de dou liste n
care primul element al perechii este o list care conine toate
elementele de la nceputul listei iniiale care satisfac predicatul,
al doilea element al perechii fiind lista restului de elemente din
lista iniial.
definiie: span p [] = ([],[])
-
8/9/2019 Programare Functionala. Haskell
78/206
-
8/9/2019 Programare Functionala. Haskell
79/206
79
("abc", "")
sqrt
tipul: sqrt :: Floating a => a -> a
descriere: r