Sunteți pe pagina 1din 14

1.

Programare funcŃională – CHEZ SCHEME

1.1 Introducere

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, care a a
fost introdus la sfârşitul anilor 1950 de către John McCarthy. 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 ).
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.

1.2 Elementele de bază ale limbajului

• simboluri;
• expresii;
• variabile;

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>+
<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 ‘ şi ‘.

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

Exemplu:
buna Hello 653-03-439 %@=+- 3stari

1
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 |#.

6) Vectori – se definesc prin #(<lista-elemente>) sau #<numar-elemente>(<lista-


elemente>).
Exemplu:
>(define v #(a b c))
>v
#3(a b c)
>

1.2.2 Expresii

O expresie este o formată in simboluri şi funcŃii sau operatori. O funcŃie în Clips


este o secvenŃă de cod executabil identificată printr-un nume şi care întoarce o valoare
sau are ca rezultat un efect vizibil. În Chez Scheme funcŃiile sunt scrise în format
predefinit.
Exemplu: (+ 3 4 5) sau (+ 3 ( * 4 8) 9)

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)
>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”
>

Pentru a afişa numele unei variabile şi nu valoarea acesteia, se foloseşte funcŃia quote cu
sintaxa:

2
(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
>’s
s

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
>

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.

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

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.

3
• +
(+ <expresie-numerică> <expresie-numerică>+)
Întoarce suma primului argument cu următoarele argumente.

• -
(- <expresie-numerică> <expresie-numerică>+)
Întoarce primul argument minus argumentele următoare.

• *
(* <expresie-numerică> <expresie-numerică>+)
Întoarce produsul argumentelor.

• /
(/ <expresie-numerică> <expresie-numerică>+)
Întoarce primul argument împărŃit la argumentele următoare.

• <, >
(< <expresie-numerică> <expresie-numerică>+)
Întoarce #t dacă fiecare argument este mai mic (mai mare) în valoare decât argumentul
următor. Altfel, întoarce #f.

• <=, =>
(<= <expresie-numerică> <expresie-numerică>+)
Întoarce #t dacă valoarea fiecărui argument este mai mică (mai mare) sau egală cu
valoarea argumentului următor. Altfel, întoarce #f.

• =
(= <expresie-numerică> <expresie-numerică>+)
Întoarce #t dacă valoarea primului argument este egală cu valorile argumentelor
următoare. Altfel întoarce #f. Se aplică numai pentru compararea expresiilor numerice.

• abs
(abs <expresie-numerică>)
Întoarce valoarea absolută a argumentului său.

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

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

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

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

• procedure?
(procedure? <expresie >)
Verifică dacă argumentul este identificatorul unei proceduri.

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

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

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

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

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

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

• eq
(eq? <expresie> <expresie>+)
Întoarce #t dacă primul argument este egal ca valoare cu toate celelalte argumente şi
toate argumentele sunt simboluri.

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

• equal
(equal? <expresie> <expresie>+)
Întoarce #t dacă primul argument este echivalent ca tip şi ca valoare cu toate celelalte
argumente.

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)
(boolean? #t)
5
(eq? 'adf 'afghg)
(equal? 2 2.0)

6
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 legate una de cealaltă prin
câmpul cdr. 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ă.
Predicate de lucrul cu listele sunt:
• list
(list <element1> <element2>...)
Alcătuieşte o listă din elementele specificate ca argumente.

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

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

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

• cxxxxr
(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?
(null? <lista>)
Verifică dacă o listă este vidă.

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

• set-car!
(set-car! <lista> <obiect>)
Înlocuieşte capul listei cu obiectul specificat ca argument.

• set-cdr!
(set-car! <lista> <obiect>)
Înlocuieşte coada listei cu obiectul specificat ca argument.

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

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

• list-tail
(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
(append <lista> …)
Adaugă elemente la coada listei specificate.

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

• member
(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
(last-pair <lista>)
Întoarce ultimul element al unei liste.

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

• make-list
(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
(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.

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

• subst
(subst <element-nou> <element-vechi> <lista>)
Înlocuieşte în listă elementul vechi cu unul nou.

8
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)
a
>(crd 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))
>(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)

Teme:
1. Să se realizeze intersecŃia, reuniunea a două liste.
2. Să se întoarcă primele n elemente ale unei liste.
3. Să se verifice dacă o listă e inclusă în altă listă.
4. Să se elimine elementele care se repetă într-o listă.
5. Să se introducă o sublistă într-o listă de la o poziŃie n specificată.
6. Ordonarea unei liste.

9
1.5 FuncŃ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>...)
• 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.
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

În ChezScheme procedurile sunt considerate obiecte şi se poate atribui o procedură ca


valoare a unei variabile pentru a putea fi utilizată în mai multe situaŃii.
Exemplu: de definire a funcŃiei f(x)=x+3
(define f1
(lambda (x)
(+ x 3))
)

(define f2
(lambda (x y)
(+ x y))
)

(define f3
(lambda ()
(+ 4 3))
)

(define f4
(lambda x
x))
(define f5
(lambda (w . z)
(list w z))
)

10
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))
(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).

Exemplu: de aplicare a funcŃiilor de comparare între valori numerice


(define f (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) (f < 2 3))
(cons '(2 = 3 este) (f = 2 3))
(cons '(2 > 3 este) (f > 2 3))
))
;lista l contine trei subliste, fiecare continand operandul,
;operatorii si rezultatul obtinut in urma aplicarii functiei f

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ă.

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

• record-case
(record-case <expresie> <clauza1> <clauza2>...)
<clauza> ::= ((<cheie> ...) <variabile-formale> <exp1 ><exp2 >...)
Ultima clauză poate avea şi forma:
<clauza> ::= (else <exp1 ><exp2 >...)

11
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.

Exemple:
• exemplu 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)))

• exemplu 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)) )
;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
>(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

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


2005

12
m c m x c x v
1000 100- 1000 10- 100 10 5
1000+ 900+ 90+ 10+ 5
=2005

• exemplu 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)
(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))
))

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

• exemplu 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
;altfel calculeaza fact(i,a) unde i=i-1 si a=a*i
(fact (- i 1) (* a i))
)
)
)
)

13
;calculeaza factorialul recursiv n!=n*(n-1)!
(define factorial2
(lambda (n)
;initializeaza fact(i) unde i=n
(let fact ((i n))
(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)))
)
)
)
)

Teme:
1. să se calculeze recursiv şi iterativ Cn,m.
2. să se realizeze o funcŃie care să scrie un număr cu cifre romane.
3. să se afişeze şirul lui Fibonacci.
4. să se scrie funcŃiile lui Hofstadter pentru 0<=n=<12 unde f(n)=n-g(f(n-1)) şi
g(n)=n-f(g(n-1))

14

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