Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pp:21:laboratoare:racket:intro [2021/02/26 14:06]
andrei.olaru created
pp:21:laboratoare:racket:intro [2021/03/15 10:36] (current)
bot.pp
Line 1: Line 1:
-== Introducere în Racket ==+====== Racket: ​Introducere ​====== 
 + 
 +  * Data publicării:​ 28.02.2021 
 +  * Data ultimei modificări:​ 28.02.2021 
 + 
 +===== Obiective ===== 
 + 
 +Scopul acestui laborator este introducerea ​în **programarea funcțională** și prezentarea elementelor de bază ale limbajului **Racket**. 
 + 
 +Aspectele urmărite sunt: 
 + 
 +  * particularitățile paradigmei **funcționale** în raport cu celelalte paradigme de programare studiate: calculul modelat ca o compunere de funcții, fără secvențe de instrucțiuni,​ fără cicluri, fără atribuiri (și fără efecte laterale în general) 
 +  * programarea în **Racket**: primitivele limbajului, definirea de noi expresii și funcții 
 +  * **modelul de evaluare** a expresiilor în Racket: modelul substituției + evaluare aplicativă 
 +  * **legătura** TDA (tipuri de date abstracte) - recursivitate - programare funcțională - demonstrații de corectitudine 
 + 
 +===== Particularități ale paradigmelor de programare studiate ===== 
 + 
 +Există moduri diferite de a programa un calculator pentru a rezolva o anumită problemă. Vom înțelege prin paradigmă o școală de gândire referitor la organizarea procesului de calcul într-un limbaj de programare. 
 + 
 +Vom relua, în ordinea în care au fost studiate, cele două paradigme de programare întâlnite până acum: 
 + 
 +  * paradigma **procedurală** 
 +  * paradigma **orientată obiect** 
 + 
 +și vom trece în revistă caracteristicile acestora urmând să prezentăm apoi paradigma **funcțională**. 
 + 
 +==== Programarea procedurală ==== 
 + 
 +În programarea procedurală,​ elementul de bază este **procedura**. Programul constă într-o succesiune de apeluri de proceduri (fie că sunt primitive ale limbajului, fie că sunt definite de programator,​ caz în care pot conține la rândul lor alte apeluri de proceduri, ș.a.m.d.). 
 + 
 +Observăm:​ 
 + 
 +  * caracterul **imperativ** al apelurilor: inițializează(!),​ calculează(!),​ dealocă(!) 
 +  * calculele se realizează folosind **efecte laterale** (side-effects):​ Spunem că o procedură are efecte laterale dacă nu doar întoarce un rezultat, ci și modifică starea unor entități din exteriorul ei, de exemplu valorile unor variabile sau structuri reținute în memorie. 
 + 
 +==== Programarea orientată obiect ==== 
 + 
 +Programarea orientată obiect mută centrul de interes de la procedură (secvență de prelucrare) la structura de date prelucrată. Elementul de bază este **obiectul**,​ și fiecare obiect încapsulează metode specifice prin care poate fi modificat/​prelucrat. 
 + 
 +Și în programarea orientată obiect prelucrarea se bazează pe calcule cu **efecte laterale**: o metodă modifică, de regulă, starea obiectului pe care a fost apelată. 
 + 
 +==== Programarea funcțională ==== 
 + 
 +Una dintre principalele diferențe aduse de programarea funcțională este absența **efectelor laterale**, și se datorează faptului că programarea funcțională este atemporală. Nu există atribuiri, nu există secvență de comenzi, o anumită expresie are o singură valoare pe tot parcursul programului. Elementul central este **funcția** (văzută însă nu în sens procedural, ci mai degrabă în sens matematic). Programele constau în compuneri și aplicări de funcții. Exemplu (limbajul ''​%%Haskell%%''​):​ 
 + 
 +<​code>​ 
 +insertion_sort [] = []  
 +insertion_sort (x:xs) = insert x (insertion_sort xs) 
 + 
 +insert y [] = [y]  
 +insert y (x:xs) = if y < x then (y:x:xs) else x:(insert y xs) 
 + 
 +</​code>​ 
 +Observați asemănarea izbitoare între codul Haskell și **definirea axiomelor unui TDA**, studiată la cursul de //Analiza Algoritmilor//​ funcțiile sunt definite pe cazurile de aplicare și folosesc recursivitatea pentru a referi un caz deja implementat. 
 + 
 +Funcția **''​%%insertion_sort%%''​** primește o listă ca parametru, și o sortează prin inserție. Dacă parametrul lui //​insertion_sort//​ este lista vidă, atunci funcția va întoarce lista vidă (care este sortată trivial). Altfel, ''​%%insertion_sort%%''​ sortează recursiv sublista ''​%%xs%%'',​ apoi inserează pe poziția corespunzătoare în această listă (sortată) elementul ''​%%x%%''​. 
 + 
 +Funcția **''​%%insert%%''​** primește doi parametri:​ 
 + 
 +  * un element 
 +  * o listă sortată. 
 + 
 +Dacă lista primită ca parametru este vidă, ''​%%insert%%''​ întoarce o listă cu un singur element (primul parametru). Altfel, introduce elementul ''​%%y%%''​ în listă, astfel încât sortarea să se conserve. 
 + 
 +Exemplul de mai sus este sugestiv pentru modul de construcție a programelor funcționale:​ rezultatul final se obține din rezultate intermediare,​ prin **compuneri și aplicări** de funcții. Definind funcția ''​%%insertion_sort%%''​ pe o listă nevidă, am observat că avem nevoie întâi să sortăm recursiv lista fără primul element, apoi să inserăm primul element "la locul lui" în lista sortată. Programarea funcțională **nu** ne permite secvențe de instrucțiuni de tipul "​întâi fă asta, apoi fă cealaltă",​ așa că am reformulat secvența de comenzi într-o secvență de aplicări de funcții: "​inserează primul element în rezultatul obținut prin sortarea restului"​. Nu este nicio problemă că încă nu avem o funcție care inserează un element într-o listă sortată; de fiecare dată când avem nevoie de un rezultat încă necalculat, ne putem **imagina** (//wishful thinking//) că avem deja o funcție care realizează calculul respectiv și putem apela acea funcție, urmând să o implementăm ulterior. Exact așa am procedat cu funcția ''​%%insert%%''​. Această abordare ne face să spunem că programarea funcțională este de tip //​[[https://​en.wikipedia.org/​wiki/​Wishful_thinking|wishful thinking]]//​. 
 + 
 +Observați în exemplul de mai sus și **absența** efectelor laterale. Niciuna dintre funcții nu modifică zone de memorie din afara acesteia. 
 + 
 +===== Racket ===== 
 + 
 +  * **Racket** este un limbaj: 
 +    * derivat din **[[https://​en.wikipedia.org/​wiki/​Lisp_(programming_language)|Lisp]]** -- sintaxă pe bază de paranteze. 
 +    * tipat dinamic -- tipul valorilor nu este verificat la compilare. 
 +    * tipat tare (strong) -- valorile nu se convertesc automat la tipul cerut de situație. 
 +    * cu evaluare aplicativă -- argumentele funcțiilor sunt evaluate înainte de aplicarea funcției. 
 +    * multiparadigmă (suportă programarea funcțională dar are și constructe imperative, pe care noi nu le vom folosi) 
 +    * înrudit cu [[http://​www.racket-lang.org/​new-name.html|Scheme]] 
 + 
 +În centrul limbajului Racket se află **evaluarea expresiilor** constând în **aplicări de funcții**. Fie următoarea aplicare a funcției //max//, în **C**: 
 + 
 +<code c> 
 +int result; 
 +result = max (3,4); 
 + 
 +</​code>​ 
 +Comparativ, iată aplicarea funcției ''​%%max%%''​ în ''​%%Racket%%'':​ 
 + 
 +<code lisp> 
 +(max 3 4) 
 + 
 +</​code>​ 
 +În **C**, apelul de funcție se realizează direct prin nume; acesta este urmat de paranteze, iar între paranteze sunt enumerați parametrii. În **Racket**, paranteza deschisă indică exclusiv faptul că urmează o **aplicare de funcție**. Între paranteze se află numele funcției, urmat de parametri. Următoarea construcție:​ 
 + 
 +<code lisp> 
 +(max (+ 2 3) 4) 
 + 
 +</​code>​ 
 +se interpretează astfel: 
 + 
 +  * evaluează aplicarea funcției ''​%%max%%'',​ care primește doi parametri 
 +  * primul parametru reprezintă o altă aplicare a funcției ''​%%+%%''​ cu parametrii ''​%%2%%''​ și ''​%%3%%''​ 
 +  * aplicarea funcției ''​%%+%%''​ se evaluează la 5 
 +  * funcția ''​%%max%%''​ determină maximul între două numere, iar aplicarea ei pe ''​%%5%%''​ și ''​%%4%%''​ se evaluează la 5. 
 + 
 +Racket este un limbaj în care argumentele sunt transmise funcției prin valoare (**call-by-value**),​ astfel că prima expresie evaluată în exemplul de mai sus este (+ 2 3). 
 + 
 +Următoarea construcție:​ 
 + 
 +<code lisp> 
 +(max (3) 4) 
 + 
 +</​code>​ 
 +este **invalidă**. 
 + 
 +**Racket** va interpreta ''​%%(3)%%''​ ca pe o tentativă de a aplica funcția cu numele ''​%%3%%''​ pe zero parametri. Codul va genera eroare. 
 + 
 +**Exercițiu**:​ încercați să priviți orice construcție din limbajul Racket ca pe o funcție. De exemplu, ''​%%if%%'':​ 
 + 
 +<code lisp> 
 +(if (= 2 3) 2 (max 2 3)) 
 + 
 +</​code>​ 
 +Putem observa faptul că ''​%%if%%''​ se comportă ca o funcție cu trei parametri:​ 
 + 
 +  * primul parametru este **condiția**. Condiția reprezintă o aplicare de funcție, care întoarce (se evaluează la) ''​%%true%%''​ sau ''​%%false%%''​ 
 +  * al doilea parametru reprezintă expresia de evaluat când condiția este ''​%%true%%''​ 
 +  * al treilea parametru reprezintă expresia de evaluat când condiția este ''​%%false%%''​ 
 + 
 +Cum 2 este diferit de 3, codul de mai sus va întoarce al treilea parametru (evaluarea expresiei ''​%%(max 2 3)%%''​). 
 + 
 +===== Tipuri de date ===== 
 + 
 +Următoarele tipuri de date sunt uzuale în **Racket** (cu **bold** tipurile pe care le vom folosi mai mult: 
 + 
 +  * Tipuri de date **simple** 
 +    * **Boolean** -- ''​%%#​t%%''​ sau ''​%%#​f%%''​ 
 +    * **Număr** 
 +    * **Simbol** (literal) -- ''​%%'​a%%'',​ ''​%%'​b%%'',​ ''​%%'​ceva%%'',​ ''​%%'​un-simbol-din-mai-multe-cuvinte%%'',​ ... 
 +  * Caracter 
 +  * Tipuri de date **compuse** 
 +    * **Pereche** 
 +    * **Listă** 
 +    * Șir de caractere (String) 
 +    * Vector 
 + 
 +==== Simboluri ==== 
 + 
 +Simbolurile (numite și literali) sunt valori formate din unul sau mai multe caractere, fără spațiu. Diferențierea dintre un nume (care este legat la o valoare, similar unei variabile din limbajele imperative) și un simbol se face atașând în fața valorii simbolului un apostrof: ''​%%'​simbol%%''​. 
 + 
 +**Atenție!** Apostroful în fața unui simbol (sau, vedem mai jos, a unei expresii în paranteză) este un operator, echivalent cu funcția ''​%%quote%%'',​ care determină ca simbolul sau expresia care îi urmează să **nu** fie evaluată. Astfel, ''​%%'​simbol%%''​ se valuează la un simbol și nu se încearcă evaluarea unei variabile cu numele ''​%%simbol%%''​ și găsirea unei valori asociate acestui nume. 
 + 
 +==== Perechi ==== 
 + 
 +O pereche este un tuplu de două elemente, care pot avea tipuri diferite. Pentru manipularea perechilor, Racket ne pune la dispoziție un constructor (''​%%cons%%''​) și doi selectori (''​%%car%%''​ și ''​%%cdr%%''​). Utilizarea acestora este demonstrată în exemplele de mai jos: 
 + 
 +<code lisp> 
 +(cons 1 2) ; construiește perechea (1 . 2) 
 + 
 +(car (cons 1 2)) ; întoarce primul element din pereche, adică 1 (cdr (cons 1 2)) ; întoarce al doilea element din pereche, adică 2 
 + 
 +(cons 3 (cons 1 2)) ; construiește PERECHEA (3 . (1 . 2)) (primul element al perechii este un număr, al doilea este o pereche) 
 + 
 +(cdr (cons 3 (cons 1 2))) ; întoarce perechea (1 . 2) 
 + 
 +</​code>​ 
 +==== Liste ==== 
 + 
 +Denumirea "​Lisp"​ a limbajului părinte al Racket-ului provine de la "List Processing",​ și într-adevăr lista este o structură de bază în cele două limbaje. Mulțumită faptului că se pot construi perechi eterogene (între elemente de tipuri diferite, de exemplu între un element și o listă), Racket implementează orice listă nevidă ca pe o pereche între primul element și restul listei. Astfel, tipul //listă// **împrumută** de la tipul //pereche// constructorul ''​%%cons%%''​ și selectorii ''​%%car%%''​ și ''​%%cdr%%'',​ la care se adaugă constructorul ''​%%null%%''​ pentru **lista vidă**. Exemple: 
 + 
 +<code lisp> 
 +(cons 1 null) ; lista formata din elementul 1. Pentru ușurința citirii va fi afișată ca (1) și nu (1 . null), dar reprezintă același lucru 
 +(cons 1 (cons 2 (cons 3 null))) ; lista (1 2 3) 
 +(cons 'a (cons 'b (cons 'c '()))) ; lista (a b c); Atenție, lista vidă se poate reprezenta ca '() sau null; 
 + 
 +</​code>​ 
 +**Funcția** ''​%%list%%''​ construiește o listă nouă care va conține elementele date ca argumente funcției:​ 
 + 
 +<code lisp> 
 +(list 1 2 3 4) 
 + 
 +</​code>​ 
 +Astfel, putem construi lista (1 2 3 4) fie folosind apostroful -- ''​%%'​(1 2 3 4)%%''​ -- fie folosind funcția ''​%%list%%''​ -- ''​%%(list 1 2 3 4)%%'',​ dar apostroful nu poate substitui oricând funcția ''​%%list%%'',​ după cum se observă în exemplele de mai jos: 
 + 
 +<code lisp> 
 +(list 1 2 (+ 2 3)) ; se evaluează la lista (1 2 5) 
 +'(1 2 (+ 2 3)) ; se evaluează la lista (1 2 (+ 2 3)), pentru că întreaga expresie de după apostrof nu se evaluează. 
 + 
 +</​code>​ 
 +=== Operatori pe liste === 
 + 
 +<code lisp> 
 +(car '(1 2 3 4)) ; întoarce 1, adică primul element din perechea formată din elementul 1 și lista (2 3 4) 
 +(cdr '(1 2 3 4)) ; întoarce (2 3 4), adică al doilea element din perechea formată din elementul 1 și lista (2 3 4) 
 + 
 +</​code>​ 
 +Funcțiile ''​%%car%%''​ și ''​%%cdr%%''​ pot fi compuse pentru a obține diverse elemente ale listei. Exemple: 
 + 
 +<code lisp> 
 +(car (cdr '(1 2 3 4 5))) ; întoarce 2 
 +(cdr (car '(1 2 3 4 5))) ; cum (car list) nu întoarce o listă, ci un element, apelul produce eroare; funcția cdr așteaptă liste ca parametru 
 +(cdr (cdr '(1 2 3 4 5))) ; întoarce (3 4 5)` `(car (cdr (cdr '(1 2 3 4 5))) ; întoarce 3 
 + 
 +</​code>​ 
 +Racket permite forme prescurtate pentru compuneri de funcții de tip ''​%%car%%''​ și ''​%%cdr%%''​. Rescriem exemplele de mai sus folosind aceste forme prescurtate:​ 
 + 
 +<code lisp> 
 +(cadr '(1 2 3 4 5)) ; întoarce 2 
 +(cdar '(1 2 3 4 5)) ; produce eroare 
 +(cddr '(1 2 3 4 5)) ; întoarce (3 4 5)   
 +(caddr '(1 2 3 4 5)) ; întoarce 3 
 + 
 +</​code>​ 
 +Alte funcții utile pentru manipularea listelor: 
 + 
 +<code lisp> 
 +(append '(1 2 3) '(4) '(5 6)) ; întoarce lista (1 2 3 4 5 6), din concatenarea tuturor listelor primite ca argumente 
 +(null? '()) ; întoarce #t (adică true), întrucât lista primită ca argument este vidă 
 +(null? '(1 2) ; întoarce #f (adică false), întrucât lista primită ca argument este nevidă 
 +(length '(1 2 3 4)) ; întoarce 4 (lungimea listei primită ca argument) 
 +(length '(1 (2 3) 4)) ; întoarce 3 (lungimea listei primită ca argument) 
 +(reverse '(1 (2 3) 4)) ; întoarce (4 (2 3) 1), adică lista primită ca argument - cu elementele în ordine inversă 
 +(list? '()) ; întoarce #t, întrucât argumentul este o listă` 
 +(list? 2) ; întoarce#​f,​ întrucât argumentul nu este o listă 
 + 
 +</​code>​ 
 +===== Legarea variabilelor ===== 
 + 
 +Un identificator poate fi legat la o valoare folosind (printre altele) construcția ''​%%(define identificator valoare)%%''​. Efectul ''​%%define%%''​-ului este de a permite referirea unei expresii (adesea complexă) cu ajutorul unui nume concis, nu acela de a atribui o valoare unei variabile. În urma ''​%%define%%''​-urilor **nu** se suprascriu valori la anumite locații din memorie. Într-un program Racket nu se poate face ''​%%define%%''​ de mai multe ori la același simbol). 
 + 
 +<code lisp> 
 +(define x 2) ; x devine identificator pentru 2 
 +(define y (+ x 2)) ; y devine identificator pentru 4, întrucât x este doar un alt nume pentru valoarea 2 
 +(define my_list '(a 2 3)) ; my_list identifică lista (a 2 3) 
 +(car my_list) ; intoarce a (+ (cadr my_list) y) ; întoarce suma dintre al doilea element din lista my_list și y, deci 2 + 4 = 6 
 + 
 +</​code>​ 
 +===== Funcții anonime (lambda) ===== 
 + 
 +O funcție anonimă se definește utilizând cuvântul cheie ''​%%lambda%%''​. Sintaxa este: ''​%%(lambda (arg1 arg2 ...) ce_întoarce_funcția)%%''​. 
 + 
 +<code lisp> 
 +(lambda (x) x) ; funcția identitate 
 +; pentru a aplica această funcție procedăm în felul următor: 
 +((lambda (x) x) 2) ; întoarce 2; se respectă sintaxa cunoscută (funcție arg1 arg2 ...) 
 + 
 +(lambda (x y) (+ x y)) ; funcție care calculează suma a doi termeni x și y 
 +(lambda (l1 l2) (append l2 l1)) ; funcție care concatenează listele l2 și l1, începând cu l2 
 + 
 +</​code>​ 
 +Este destul de neplăcut să rescriem o funcție anonimă pentru a o utiliza în mai multe locuri. Folosim ''​%%define%%''​ când dorim să legăm un identificator la o funcție anonimă (în aparență funcția nu mai este anonimă). 
 + 
 +<code lisp> 
 +(define identitate (lambda (x) x)) 
 +(identitate 3) ; întoarce 3 
 + 
 +</​code>​ 
 +Limbajul ne permite să condensăm definirea unei funcții cu legarea ei la un nume, scriind ca ''​%%(define (nume-funcție arg1 arg2 ...) ce_întoarce_funcția)%%'':​ 
 + 
 +<code lisp> 
 +(define (identitate x) x) 
 +(identitate 3) ; întoarce 3 
 + 
 +(define append2 (lambda (l1 l2) (append l2 l1))) (append2 '(1 2 3) '(4 5 6)) ; întoarce lista (4 5 6 1 2 3) 
 +; fără '​lambda'​ 
 + 
 +(define (append2 l1 l2) (append l2 l1)) (append2 '(1 2 3) '(4 5 6)) ; întoarce lista (4 5 6 1 2 3) 
 + 
 +</​code>​ 
 +Putem oricând scrie ''​%%λ%%''​ în loc de ''​%%lambda%%''​ (folosind ''​%%Ctrl+\%%''​). 
 + 
 +===== Funcții utile ===== 
 + 
 +Operatori:​ 
 + 
 +  * aritmetici: ''​%%+%%'',​ ''​%%-%%'',​ ''​%%*%%'',​ ''​%%/​%%'',​ ''​%%modulo%%'',​ ''​%%quotient%%''​ 
 +  * relaționali:​ ''​%%<​%%'',​ ''​%%<​=%%'',​ ''​%%>​%%'',​ ''​%%>​=%%'',​ ''​%%=%%'',​ ''​%%eq?​%%'',​ ''​%%equal?​%%''​ 
 +  * logici: ''​%%not%%'',​ ''​%%and%%'',​ ''​%%or%%''​ 
 + 
 +<code lisp> 
 +(modulo 5 2) ; 1, restul împărțirii lui 5 la 2 
 +(quotient 5 2) ; 2, împărțire întreagă 
 + 
 +(< 3 2) ; #f 
 +(>= 3 2) ; #t 
 +(= 1 1) ; #t, verifică egalitatea între numere 
 +(= '(1 2) '(1 2)) ; eroare 
 +(equal? '(1 2) '(1 2)) ; #t, verifică egalitatea între valori 
 +(eq? '(1 2 3) '(1 2 3)) ; #f, asemănător cu "​=="​ din Java, verifică dacă două obiecte referă aceeași zonă de memorie 
 + 
 +(define x '(1 2 3)) 
 +(eq? x x) ; #t 
 + 
 +</​code>​ 
 +Expresii condiționale:​ 
 + 
 +  * ''​%%if%%''​ 
 +  * ''​%%cond%%''​ 
 + 
 +<code lisp> 
 +; (if testexp thenexp elseexp) ; sau fără bucata de else 
 +(if (< a 0) 
 +    a ; întoarce a dacă a este negativ 
 +    (if (> a 10) 
 +        (* a a) ; întoarce a * a dacă a este mai mare decât 10 
 +        0)) ; întoarce 0 altfel 
 + 
 +; (cond (test1 exp1) (...) ... (else exp...) ) ; sau fără bucata de else 
 +(cond 
 +   ​((<​ a 0) a) ; întoarce a dacă a este negativ 
 +   ​((>​ a 10) (* a a)) ; întoarce a * a dacă a este mai mare decât 10 
 +   (else 0)) ; întoarce 0 altfel 
 + 
 +</​code>​ 
 +===== Cum trebuie gândit un program funcțional ===== 
 + 
 +Deși Racket este un limbaj multi-paradigmă,​ veți folosi Racket pentru a învăța să programați în spiritul programării funcționale. Sintetizăm mai jos acest spirit și modul în care puteți suplini lipsa "​uneltelor"​ cu care v-ați obișnuit în celelalte paradigme:​ 
 + 
 +  * programarea este de tip **wishful thinking** 
 +  * secvența de instrucțiuni devine **compunere de funcții** (secvență de aplicări de funcții) 
 +  * lipsesc atribuirile (variabilă = valoare) și instrucțiunile de ciclare precum ''​%%for%%''​ sau ''​%%while%%'',​ dar puteți obține același efect folosind **funcții recursive**. În loc de ciclare și atribuiri (adică în loc să ținem starea curentă a problemei în variabile), vom folosi recursivitate și starea curentă a problemei se va pasa ca parametru în funcțiile recursive. Citiți și recitiți acest paragraf în tandem cu exemplele de mai jos. 
 +  * scrierea funcțiilor recursive derivă direct din **scrierea axiomelor** TDA-urilor implicate în problemă 
 + 
 +**Exemplul 1**: o funcție care calculează factorialul unui număr natural n. Știm că TDA-ul Natural are doi constructori de bază, ''​%%0%%''​ și ''​%%succ%%''​. Scriem axiomele operatorului factorial:​ 
 + 
 +<​code>​ 
 +factorial(0) = 1 
 +factorial(succ(n)) = succ(n) * factorial(n) 
 + 
 +</​code>​ 
 +Traducem întocmai aceste axiome în cod Racket: 
 + 
 +<code lisp> 
 +(define (factorial n) ; identificatorul factorial primește un parametru și anume numărul natural n 
 +      (if (= n 0) ; cazul de bază, n=0, corespunzător primei axiome 
 +          1  ; în acest caz, valoarea întoarsă este 1 
 +          (* n (factorial (- n 1))))) ; altfel, rezultatul este n * factorial(n - 1), corespunzător celei de-a doua axiome 
 + 
 +; observați că nu reținem în nicio variabilă n-ul la care am ajuns, dar el este trimis ca parametru dintr-un apel recursiv în altul 
 + 
 +(factorial 0) ; întoarce 1 
 +(factorial 1) ; întoarce 1 
 +(factorial 2) ; întoarce 2 
 +(factorial 3) ; întoarce 6 
 +(factorial -1) ; intră în buclă infinită 
 + 
 +</​code>​ 
 +**Exemplul 2**: o funcție care calculează suma elementelor dintr-o listă L. Știm că TDA-ul List are doi constructori de bază, ''​%%null%%''​ și ''​%%cons%%''​. Scriem axiomele operatorului sum-list: 
 + 
 +<​code>​ 
 +sum-list(null) = 0 
 +sum-list(cons(a,​L)) = a + sum-list(L) 
 + 
 +</​code>​ 
 +Trecem axiomele în cod Racket: 
 + 
 +<code lisp> 
 +(define (sum-list L); identificatorul sum-list care primește un parametru, lista L 
 +    (if (null? L) ; dacă L este vidă 
 +        0  ; întoarce 0, cazul corespunzător primei axiome 
 +        (+ (car L) (sum-list (cdr L))))) ; altfel întoarce primul element + sum-list(restul listei), corespunzător celei de-a doua axiome 
 + 
 +(sum-list '(1 2 3)) ; întoarce 6 
 +(sum-list 1) ; eroare 
 + 
 +</​code>​ 
 +===== Resurse ===== 
 + 
 +  * [[https://​ocw.cs.pub.ro/​courses/​_media/​pp/​21/​laboratoare/​racket/​intro-skel.zip|Schelet]] 
 +  * [[https://​ocw.cs.pub.ro/​courses/​_media/​pp/​21/​laboratoare/​racket/​intro-sol.zip|Soluții]] 
 +  * [[https://​github.com/​cs-pub-ro/​PP-laboratoare/​raw/​master/​racket/​intro/​racket-cheatsheet-1.pdf|Cheatsheet]] 
 + 
 +===== Referinţe ===== 
 + 
 +  * [[http://​docs.racket-lang.org/​reference/​index.html|Documentație Racket]], în special funcțiile pentru [[http://​docs.racket-lang.org/​reference/​booleans.html|valori booleene]], [[http://​docs.racket-lang.org/​reference/​numbers.html|numere]] și [[http://​docs.racket-lang.org/​reference/​pairs.html|perechi/​liste]] 
 +  * [[https://​en.wikipedia.org/​wiki/​Functional_programming|Programare funcțională]] 
 +  * [[https://​en.wikipedia.org/​wiki/​Recursive|Recursivitate]] 
 +  * [[https://​en.wikipedia.org/​wiki/​Lisp_programming_language|Lisp]] 
 +  * [[https://​en.wikipedia.org/​wiki/​Racket_(programming_language)|Racket]] 
 +  * [[https://​en.wikipedia.org/​wiki/​Dynamic_typing#​Dynamic_typing|Tipare dinamică]] 
 +  * [[https://​en.wikipedia.org/​wiki/​Strong_typing|Tipare strong]] 
 +  * [[https://​en.wikipedia.org/​wiki/​Eager_evaluation|Evaluare aplicativă]] 
 +  * [[https://​www.gnu.org/​software/​mit-scheme/​documentation/​stable/​mit-scheme-user/​Coding-style.html|Scheme coding style]] 
  
-TODO 
pp/21/laboratoare/racket/intro.1614341178.txt.gz · Last modified: 2021/02/26 14:06 by andrei.olaru
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0