Sunteți pe pagina 1din 60

Programare funcţională

Exemple de variante Lisp


• ChezScheme - https://cisco.github.io/ChezScheme/
• online https://repl.it/languages/scheme

1
1.1 Introducere – CHEZ SCHEME

• Chez Scheme este un limbaj funcţional de la Cadence Research


Systems, care se bazează pe specificaţiile limbajului Scheme.
• Scheme este o variantă de Lisp.
• Are la bază teoretică calculul lambda (formalism matematic
dezvoltat de Alonzo Church).
• Dintre avantaje este faptul că nu introduce limitări ale memoriei
sau ale dimensiunii programelor.
Limbajul Chez Scheme lucrează în linie comandă. Prompt-ul care
apare la lansare este “>”. Toate comenzile trebuie introduse între
paranteze ( şi ).

2
Modul de lucru al limbajului este prin bucla read-evaluate-print,
astfel orice expresie introdusă în linie comandă este citită, apoi
evaluată şi apoi se afişează rezultatul evaluării.

3
1.2 Elementele de bază ale limbajului

• simboluri;
• expresii;
• variabile;

4
1.2.1 Tipurile primitive de date

Tipuri primitive de date sunt:


1) Integer – are următoarea sintaxă:
<integer> ::= [+ | -] <digit>+
<digit> ::= 0 | 1| 2| 3| 4| 5| 6| 7| 8| 9

2) Float – cu următoare sintaxă:


<float> ::= <integer> <exponent> |
<integer> . [exponent] |
. <unsigned integer> [exponent] |
<integer> . <unsigned integer> [exponent]
<unsigned-integer> ::= <digit>+

5
<exponent> ::= e | E <integer>

Un număr poate fi memorat ca float sau ca integer. Orice număr


format din semn şi cifre este memorat ca integer, iar dacă are şi punct
atunci este memorat ca float.

3) Simbol – este alcătuit dintr-o secvenţă de caractere care începe cu


un caracter ASCII printabil şi este urmat de zero sau mai multe
caractere ASCII printabile. Simbolul se termină la întâlnirea oricărui
caracter printabil (spaţiu, tab, return, line feed, ghilimele, paranteze
(), &, |, <, ~, ;). Pentru a afişa un simbol acesta trebuie scris între ‘ ‘.
(define v ’fgte344)

6
Chez Scheme nu este „case sensitive” (nu face distincţie între literele
mici si literele mari).

4) String – este un set de caractere care începe cu ghilimele ” şi


este urmat de zero sau mai multe caractere printabile şi se termină cu
”.

5) Comentariile – încep cu ; şi se continuă până la sfârşitul de rând


sau dacă se află pe mai multe rânduri sunt încadrate de #| şi |#.

7
6) Vectori – se definesc prin #(<lista-elemente>) sau
#<numar-elemente>(<lista-elemente>).
Exemplu:
>(define v ’#(a b c))
>v
#3(a b c)
>

7) Numere complexe – se definesc în forma rectangulară x+yi sau


polară magnitudine@unghi, unde magnitudine= , unghi în
intervalul (-π, + π)
(make-rectangular -1 2) -1+2i
(make-polar 1.0 (asin -1.0)) 0.0-1.0i
Exemplu:

8
>(define c1 (make-rectangular -1 1))
> c1
-1+1i
>(define c2 (make-rectangular -1 -1))
> c2
-1-1i
> (complex? c1)
#t
> (real-part c1)
-1
> (imag-part c1)
1
> (define c2 (make-rectangular -1.0 -1.0))
> c2

9
-1.0-1.0i
> (- c1 c2)
0.0+2.0i
> (+ c1 c2)
-2.0+0.0i
>


8) Numere raționale – se definesc (/ <numar1>) pentru sau


(/ <numar1> <numar2> <numar3>...) pentru .
 ∗
∗…

> (/ 3)
1/3
> (/ -3)

10
-1/3
> (/ 4 6)
2/3
> (/ 2)
1/2
> (/ 2.0)
0.5
> (/ 2+2i 2-2i)
0+1i
> (/ 2 2-2i)
1/2+1/2i
> (/ 2+2i 2)
1+1i

11
> (numerator 4)
4
> (denominator 4)
1
> (numerator 2/4)
1
> (denominator 2/4)
2
> (numerator 2/3)
2
> (denominator 2/3)
3
> (numerator 4.0)
4.0

12
> (denominator 4.0)
1.0
> (numerator 0.25)
1.0
> (denominator 0.25)
4.0
> (numerator -1.25)
-5.0
> (denominator -1.25)
4.0

13
1.2.2 Expresii

O expresie este o formată in simboluri şi funcţii sau operatori.


În Chez Scheme funcţiile sunt scrise în format prefixat.
Exemplu: (+ 3 4 5) sau (+ 3 ( * 4 8) 9)

14
1.2.3 Variabile

Variabilele se definesc cu ajutorul funcţiei define cu următoarea


sintaxă:
(define <nume-variabila> <valoare>)
Numele de variabilă trebuie să fie un simbol, iar valoarea poate fi
numerică, simbol (caz în care valoarea este precedată de ’ sau şir de
caractere cuprins între “ ”).
Exemple:
>(define a 34)
>a
34
>(define a 12.56)

15
>a
12.56
>(define a 3.e-2)
>a
0.03
>(define sir ’sirul)
>sir
sirul
>(define s ”sir de caractere”)
>s
„sir de caractere”
>

16
Pentru a afişa numele unei variabile şi nu valoarea acesteia, se
foloseşte funcţia quote cu sintaxa:
(quote <nume-variabila>)
Exemple:
>(quote a)
a
>(quote s)
s
Acelaşi rezultat se obţine dacă se tastează numele variabilei precedat
de ‘.
Exemple:
>’a
a

17
Pentru a iniţializa o variabilă cu o expresie înainte de a fi folosită
pentru o evaluare se foloseşte predicatul let cu următoarea sintaxă:
(let ((<variabila-1> <valoare-1>)
…(<variabila-n> <valoare-n>)…)
Variabilele utilizate în corpul predicatului let sunt variabile locale
acestuia şi nu sunt vizibile în afara lui.
Exemple:
>(let ((x 1))(+ x 1))
2
>(let ((x 1) (z 2)) (* x z))
2
>

18
1.3 Funcţii predefinite
1.3.1 Funcţii de interacţiune cu mediul
• load
(load „nume-fişier.ss”)
Încarcă constructori din fişierul specificat ca argument (împreună cu
calea de directoare).

• current-directory
(current-directory „cale-directoare”)
Setează directorul curent, care trebuie să fie precizat complet, iar
între subdirectoare se trece \\. Dacă calea de directoare nu este
precizată afişează directorul curent.

19
• exit
(exit)
Opreşte şi închide aplicaţia Scheme.

20
1.3.2 Funcţii numerice

Funcţiile numerice sunt scrise în format prefixat, cu paranteze.


Expresiile numerice pot fi şi identificatori ai unor variabile care au
valori numerice.

•+
•-
•*
•/
• <, >
• <=, =>
•=

21
• (abs <numar>)
Întoarce valoarea absolută a argumentului său.

• (exp <numar>)
Ridică numărul e la puterea indicată de argument.

• (exp <numar-1> <numar-2>))


Ridică numar-1 la puterea numar-2.

• (number? <expresie>)
Verifică dacă argumentul este număr.

22
• (symbol? <expresie>)
Verifică dacă argumentul este simbol.

• (boolean? <expresie>)
Verifică dacă argumentul are valorile de adevăr #t sau #f.

• (procedure? <expresie >)


Verifică dacă argumentul este identificatorul unei proceduri.

• (eq? <expresie> <expresie>)


Întoarce #t dacă primul argument este egal ca valoare cu toate
celelalte argumente şi toate argumentele sunt simboluri.

23
• (eqv? <expresie> <expresie>)
Întoarce #t dacă primul argument este echivalent cu toate celelalte
argumente.

• (equal? <expresie> <expresie>)


Întoarce #t dacă primul argument este echivalent ca tip şi ca valoare
cu toate celelalte argumente.

• (even? <int>)
Întoarce #t este par și #f dacă este impar.

• (odd? <int>)
Întoarce #t este impar și #f dacă este par.

24
• (log <expresie-numerică>)
Întoarce logaritmul natural al argumentului.

• (random <expresie-numerică>)
Întoarce aleator un întreg cu o valoare în intervalul 0 şi argument.

• (sqrt <expresie-numerică>)
Întoarce rădăcina pătrată a argumentului.

• (round <expresie-numerică>)
Rotunjeşte argumentul către cel mai apropiat întreg, iar rezultatul este
un număr real.

25
• (max <expresie-numerică>+)
Întoarce valoarea celui mai mare argument numeric.

• (min <expresie-numerică>+)
Întoarce valoarea celui mai mic argument numeric.

• (gcd <intreg> ...)


Cel mai mare divisor comun al argumentelor. Rezultatul este
întotdeauna diferit de 0. La apelul fără argument returnează 0.

• (lcm <intreg> ...)


Cel mai mic multiplu comun. Rezultatul este întotdeauna diferit de 0.
La apelul fără argument returnează 1.

26
Exemple:
(- 25 5)
(/ 13 3)
(/ 13.0 -2)
(/ 13 2.0)
(truncate (/ 34 3))
(modulo 20 6)
(sqrt 16)
(cos 0.5)
(sqrt -4)
(+ (* 6 2) (/ 99 33))
(number? 2.4)
(symbol? 'bq)

27
(boolean? #t)
(eq? 'adf 'afghg)
(equal? 2 2.0)

28
1.4 Liste

Listele sunt definite între ( şi ). Dacă lista nu are nici un element


atunci este o listă vidă.
Elementele unei liste pot avea acelaşi tip de date sau tipuri diferite de
date. De asemenea, o listă poate conţine alte subliste.
Reprezentarea internă a listelor în ChezScheme este realizată prin
perechi ordonate, care conţin câmpurile car (cap) şi cdr (coadă).
Perechile sunt utilizate pentru construirea arborilor binari abstracţi.
Elementele listei ocupă câmpurile car ale fiecărei pereche. Câmpul
cdr al ultimei perechi ale unei liste construită corect din punct de
vedere sintactic şi semantic este lista vidă ( ). O listă incorectă are
orice altceva pe câmpul cdr al ultimei perechi în afară de lista vidă.

29
Predicate de lucrul cu listele sunt:
• (list <element1> <element2>...)
Alcătuieşte o listă din elementele specificate ca argumente.

• (list? <lista>)
Întoarce #t dacă lista primită ca argument este corectă.

• (car <lista>)
Întoarce primul element al unei liste, iar lista primită ca argument
nu trebuie să fie vidă.

30
• (cdr <lista>)
Întoarce coada listei, dar lista primită ca argument nu trebuie să fie
vidă.

• (cxxxr <lista> )
xxx pot fi a sau r şi se obţine o combinaţie a predicatelor car şi cdr.
De exemplu cadr înseamnă (car (cdr ...)), sau caadr este echivalent
cu (car (car (cdr ...))).

• (null? <lista>)
Verifică dacă o listă este vidă.

31
• (cons <lista1> <lista2>)
Construieşte o listă prin concatenarea argumentelor.

• (set-car! <lista> <obiect>)


Înlocuieşte capul listei cu obiectul specificat ca argument.

• (set-cdr! <lista> <obiect>)


Înlocuieşte coada listei cu obiectul specificat ca argument.

• (length <lista> )
Întoarce lungimea listei.

32
• (list-ref <lista> <pozitie>)
Întoarce elementul de pe poziţia specificată ca argument.
Numerotarea elementelor unei liste începe cu poziţia 0.

• (list-tail <lista> <pozitie>)


Întoarce o sublistă începând cu elementul specificat ca poziţie şi
până la sfârşitul listei specificată ca argument.

• (append <lista> …)
Adaugă elemente la coada listei specificate.

33
• (reverse <lista>)
Creează o listă ale cărei elemente sunt elementele listei iniţiale în
ordine inversată.

• (member <obiect> <lista>)


Întoarce o sublistă al cărei cap este obiectul specificat ca argument
şi a cărei coadă este dată de celelalte elemente până la sfârşitul
listei iniţiale sau #f în alte cazuri.

• (last-pair <lista>)
Întoarce ultimul element al unei liste.

34
• (list-copy <lista>)
Întoarce o copie a listei primite ca argument.

• (make-list <numar-elemente> [<obiect>])


Creează o listă cu numărul de elemente specificat. Dacă este
specificat şi obiectul acesta va constitui elementele listei.

• (sort <predicat> <lista>)


Creează o listă ale cărei elemente sunt sortate corespunzător cu
predicatul specificat. Predicatul trebuie să poată fi aplicat tipului
de elemente ale listei.

35
• (remv <obiect> <lista>)
Şterge din listă obiectul specificat ca argument.

• (subst <element-nou> <element-vechi> <lista>)


Înlocuieşte în listă elementul vechi cu unul nou.

36
Pentru definirea unei liste de literali, elementele listei sunt scrise între
paranteze ( şi ) şi sunt precedate de apostrof ’ sau se foloseşte funcţia
quote în loc de apostrof. Atunci când o expresie este precedată de
apostrof sau de quote, expresia se numeşte expresie „quote”. O astfel
de expresie nu este considerată de către Scheme ca o procedură şi nu
sunt evaluate argumentele.
Exemplu:
>(define l1 ’(a b c d e))
>(define l2 ’())
>(null? 11)
#f
>(null? l2)
#t
>(car l1)

37
a
>(cdr l1)
(b c d e)
>(cons l2 ‘(w x z))
(() w x z)
>(cons ‘a ‘(x y))
(a x y)
>(let ((x ’(a b 3)))(set-car! x 1))
>(let ((x ’(a b 3)))(set-car! x 1) x)
(1 b 3)
>(list ‘x ‘y ‘z)
(x y z)
>(list ‘0234 l1)
(234 ( a b c d e))

38
>(list ‘aa ‘bb l1)
(aa bb ( a b c d e))
>(define x ‘(a b c d e))
>(set-car! x ‘aa)
>x
(aa b c d e)
>(set-cdr! x ‘(w ww www))
>x
(aa w ww www)
>(sort < ‘(2 6 4 0))
(0 2 4 6)

39
> (define ll2 (list '( e r t) '(2 e 4)))
> ll2
((e r t) (2 e 4))
> (define ll2 (cons '( e r t) '(2 e 4)))
> ll2
((e r t) 2 e 4)
> (define ll2 (append '( e r t) '(2 e 4)))
> ll2
(e r t 2 e 4)

40
1.5 Funţii definite de utilizator
Folosind predicatul lambda se pot defini proceduri cu un număr
specificat de argumente sau cu un număr nedefinit de argumente.
Sintaxa pentru definirea unei proceduri cu:
• n argumente este:
(lambda (<var1>... <varn>) <exp1> <exp2>...)
• un argument:
(lambda <r> <exp1> <exp2>...)

41
• n sau mai multe argumente:
(lambda (<var1>... <varn>.<r>) <exp1>
<exp2>...)
unde <var>, <var1>,..., <varn> sunt parametri formali ai
procedurii, iar <exp1> <exp2>... reprezintă corpul procedurii,
evaluate secvenţial.
<r> este un parametru formal căruia i se asociază o listă cu argumente
(în număr variabil), altele decât primii n parametri formali.
Variabilele utilizate în corpul predicatului lambda sunt variabile
locale acestuia şi nu sunt vizibile în afara lui.
Valoarea ultimei expresii evaluate este valoarea returnată de funcţie.

42
Cu ajutorul procedurii lambda nu se pot defini proceduri care acceptă
un număr variabili de argumente. Pentru a crea proceduri cu
argumente opţionale se utilizează predicatul case-lambda, împreună
cu predicate de verificare a lungimii şi predicatele car şi cdr.

Exemplu: de definire a funcţiei f(x)=x+1 în ChezScheme în linie


comandă
(lambda (x) (+ x 1) )
Pentru a apela procedura cu o anumită valoare a parametrului, se
specifică după definirea procedurii, o expresie a cărei evaluare
reprezintă valoarea parametrului. Astfel, pentru exemplu anterior
avem: ((lambda (x) (+ x 1)) (* 2 5))
În acest caz, se evaluează expresia (* 2 5)=10 şi se aplică
lambda(10)=10+1=11

43
Exemplu de calculare a elementelor unei liste cu formula x+10. Într-
un fişier se scrie:
(define f (lambda (x) (+ x 10)))
; se defineste f(x)=x+10
(define conc-list (lambda (x y) (cons x y)))
;se defineste conc-list = concatenarea lui x
si y
(define l1 (list (f (* 2 3))
(f (- 27 7))
(f (/ 27 3))))
;se defineste l1 o lista ale carei elemente
;sunt evaluari ale functiei f
(define l2 (list (f (- 12 2))
(conc-list '(a b) '(c d))

44
(f 0)
))
;l2 este o lista cu elemente de diferite
tipuri
După ce se încarcă fişierul se tastează l1 şi se obţine (16 30 19), iar
apoi l2 şi se obţine (20 ((a b) c d) 10).

20 ... 10
(...c d)
(a b)

45
Exemplu: de aplicare a funcţiilor de comparare între valori numerice
(define op (lambda (o x y) (o x y)))
;f este o functie care aplica operatorul o
asupra expresiilor x si y
;intoarce un rezultat boolean
(define l (list
(cons '(2 < 3 este) (op < 2 3))
(cons '(2 = 3 este) (op = 2 3))
(cons '(2 > 3 este) (op > 2 3))
))
;lista l contine trei subliste, fiecare
continand operandul, ;operatorii si rezultatul
obtinut in urma aplicarii functiei f

46
1.6 Structuri de control

• if
(if <test-conditie> <expresie1> <expresie2>)
Testează condiţia. Dacă este adevărată execută expresie1, altfel
execută expresie2.

• cond
(cond (<test> <expresie>) ...
[else <expresie>])
Această structură este similară cu structura „if”, cu deosebirea că
poate testa mai multe condiţii. Clauza else poate să lipsească.

47
• when
(when <test-conditie> <expresie1>
<expresie2>…)
Testează condiţia. Dacă este adevărată atunci sunt evaluate
expresie1, expresie2. Dacă evaluarea condiţiei are rezultat fals, nici
una din expresii nu mai este evaluată.

• unless
(unless <test-conditie> <expresie1>
<expresie2>…)
Testează condiţia. Dacă este adevărată atunci nu este evaluată nici
una din expresii. Dacă evaluarea condiţiei are rezultat adevărat,
atunci sunt evaluate expresiile.

48
• record-case
(record-case <expresie> <clauza1>
<clauza2>...)
<clauza> ::= ((<cheie> ...) <variabile-
formale> <exp1 ><exp2 >...)
Ultima clauză poate avea şi forma:
<clauza> ::= (else <exp1 ><exp2 >...)
Este o formă redusă a structurii switch-case din limbajele de
programare. Se evaluează <expresie> utilizând predicatul eqv?
şi se compară cu <cheie> din fiecare clauză. Dacă se găseşte o
cheie care este echivalentă cu expresia, atunci variabilele formale
primesc valorile specificate ca argumente în apelul funcţiei, iar
<exp1 ><exp2 > sunt evaluate.

49
Exemple:
• de utilizare a structurii condiţionale if. Se defineşte o funcţie
„abs” care calculează modulul unui număr introdus ca argument.
Condiţia de verificat pentru structura if este (< n 0), expresia
1 care se execută dacă evaluarea condiţiei este adevărată este (-
0 n), iar expresia 2 care se execută când condiţia este falsă
este n. Apelul funcţiei se realizează astfel (abs 30).
(define abs
(lambda (n)
(if (< n 0)
(- 0 n)
n)))

50
• de utilizare pentru record case pentru utilizarea cifrelor romane
(define cifre
(lambda (x)
(cond
((eqv? x 'I) 1)
((eqv? x 'V) 5)
((eqv? x 'X) 10)
((eqv? x 'L) 50)
((eqv? x 'C) 100)
((eqv? x 'D) 500)
((eqv? x 'M) 1000)
;daca lista are un simbol
( (null? (cdr x)) (cifre (car x)) )

51
;trateaza cazuri in care cifrele sunt in
ordine crescatoare
; de tipul IM=1000-1=999, IL=50-1=49
( (< (cifre (car x)) (cifre (cadr x)) )
(- (cifre (cdr x)) (cifre (car x))) )
;trateaza cazuri in care cifrele sunt in
ordine descrescatoare
( (> (cifre (car x)) (cifre (cadr x)) )
(+ (cifre (cdr x)) (cifre (car x))) )
)))

Apelul funcţiei se realizează astfel:


>(cifre ’i)
1

52
>(cifre ’(i))
1
>(cifre ’(i m))
999
>(cifre ’(m i))
1001

>(cifre ’(m c m x c x))


2000
m c m x c x
1000 100- 1000 10- 100 10
1000+ 900+ 90+ 10
=2000

53
>(cifre ’(m c m x c x v))
2005
m c m x c x v
1000 100- 1000 10- 100 10 5
1000+ 900+ 90+ 10+ 5
=2005

54
• de utilizare a structurilor when şi unless.
În funcţia verific1 se testează dacă „a” se află în lista x. Dacă da,
se afişează un mesaj folosind funcţia prinf, care afişează pe
monitor mesajul cuprins între ghilimele. ~s reprezintă un şablon
pentru a afişa valoarea variabilei care se află după ce s-au închis
ghilimelele. ~% reprezintă trecerea la rândul următor.
În funcţia verific2 afişează un mesaj, utilizând funcţia error.
Primul argument al funcţiei error reprezintă numele procedurii în
care a apărut eroarea, iar apoi sunt trecute argumente similar cu
sintaxa funcţiei de afişare printf.

(define verific1
(lambda (x)
(when (member 'a x)

55
(printf "'a se afla in lista ~s ~%" x))
))

(define verific2
(lambda (x)
(unless (member 'a x)
(error 'verific2 "a NU se afla in lista
~s" x))
))

56
• de utilizare a structurii record-case pentru operaţii aritmetice
(define calc
(lambda (x)
(record-case x
[(adunare) (x y) (+ x y)]
[(scadere) (x y) (- x y)]
[(inmultire) (x y) (* x y)]
[(impartire) (x y) (/ x y)]
[else (error 'calc "expresie gresita ~s"
x)])))

Apelul funcţiei calc se realizează astfel:


(calc '(adunare 5 8))
(calc '(orice 5 8))

57
• de calcul al factorialului prin metoda iterativa şi recursivă
;calculeaza iterativ factorialul pornind de
la
; definitia n! = n × (n - 1) × (n - 2) × ...
× 1
(define factorial1
(lambda (n)
;initializeaza fact(i,a) astfel i=n si a=1
(let fact ((i n) (a 1))
;variabila a memoreaza produsele intermediare
(printf "fact(i,a)=(~s ~s)~%"i a)
;daca i=0 afiseaza a
(if (= i 0) a

58
;altfel calculeaza fact(i,a) unde i=i-1 si
a=a*i
(fact (- i 1) (* a i))
)
)
)
)

;calculeaza factorialul recursiv n!=n*(n-1)!


(define factorial2
(lambda (n)
;initializeaza fact(i) unde i=n
(let fact ((i n))

59
(printf "fact(i)=(~s)~%"i)
;daca i=0 atunci afiseaza 1
;altfel afiseaza i*fact(i-1)
(if (= i 0)
1
(* i (fact (- i 1)))
)
)
)
)

60

S-ar putea să vă placă și