programare functionala. haskell

Upload: tony

Post on 29-May-2018

261 views

Category:

Documents


2 download

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