Differences

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

Link to this comparison view

pp:21:laboratoare:racket:intro [2021/02/28 17:02]
bot.pp
pp:21:laboratoare:racket:intro [2021/03/15 10:36] (current)
bot.pp
Line 1: Line 1:
 ====== Racket: Introducere ====== ====== Racket: Introducere ======
  
-  * Data publicării: ​29.02.2021 +  * Data publicării: ​28.02.2021 
-  * Data ultimei modificări: ​29.02.2021+  * Data ultimei modificări: ​28.02.2021
  
 ===== Obiective ===== ===== Obiective =====
Line 43: Line 43:
 ==== Programarea funcțională ==== ==== 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//):+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>​ <​code>​
-insertion_sort [] = [] insertion_sort (x:xs) = insert x (insertion_sort xs) +insertion_sort [] = []  
-insert y [] = [y] insert y (x:xs) = if y < x then (y:x:xs) else x:(insert y xs)+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>​ </​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. 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 **''​%%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:+Funcția **''​%%insert%%''​** primește doi parametri:
  
   * un element   * un element
   * o listă sortată.   * 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.+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]]//​.+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. Observați în exemplul de mai sus și **absența** efectelor laterale. Niciuna dintre funcții nu modifică zone de memorie din afara acesteia.
Line 75: Line 78:
     * înrudit cu [[http://​www.racket-lang.org/​new-name.html|Scheme]]     * î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**: ''​%%int result;​%%''​ ''​%%result = max (3,​4);​%%''​ Comparativ, iată aplicarea funcției //max// în //Racket//: ''​%%(max 3 4)%%''​ Î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:​ ''​%%(max (+ 2 3) 4)%%''​ se interpretează astfel:+Î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**:
  
-  * evaluează aplicarea funcției //max//, care primește doi parametri +<code c> 
-  * primul parametru reprezintă o altă aplicare a funcției ​//+// cu parametrii ​//2// și //3// +int result; 
-  * aplicarea funcției ​//+// se evaluează la 5 +result = max (3,4); 
-  * funcția ​//max// determină maximul între două numere, iar aplicarea ei pe //5// și //4// se evaluează la 5.+ 
 +</​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). 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: ​''​%%(max (3) 4)%%''​ 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.+Următoarea construcție:​
  
-**Exercițiu**:​ încercați să priviți orice construcție din limbajul Racket ca pe o funcție. De exemplu, ​//​if//: ​''​%%(if (= 2 3) 2 (max 2 3))%%'' ​Putem observa faptul că //if// se comportă ca o funcție cu trei parametri:+<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%%''​   * primul parametru este **condiția**. Condiția reprezintă o aplicare de funcție, care întoarce (se evaluează la) ''​%%true%%''​ sau ''​%%false%%''​
Line 92: Line 128:
   * al treilea parametru reprezintă expresia de evaluat când condiția este ''​%%false%%''​   * 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)//).+Cum 2 este diferit de 3, codul de mai sus va întoarce al treilea parametru (evaluarea expresiei ​''​%%(max 2 3)%%''​).
  
 ===== Tipuri de date ===== ===== Tipuri de date =====
Line 111: Line 147:
 ==== Simboluri ==== ==== 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//.+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.+**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 ==== ==== 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:+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> <code lisp>
Line 131: Line 167:
 ==== Liste ==== ==== 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: ​`+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> <code lisp>
Line 139: Line 175:
  
 </​code>​ </​code>​
-Funcția **list** construiește o listă nouă care va conține elementele date ca argumente funcției:+**Funcția** ​''​%%list%%'' ​construiește o listă nouă care va conține elementele date ca argumente funcției:
  
 <code lisp> <code lisp>
Line 145: Line 181:
  
 </​code>​ </​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:+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> <code lisp>
Line 159: Line 195:
  
 </​code>​ </​code>​
-Funcțiile ​//car// și //cdr// pot fi compuse pentru a obține diverse elemente ale listei. Exemple:+Funcțiile ​''​%%car%%'' ​și ''​%%cdr%%'' ​pot fi compuse pentru a obține diverse elemente ale listei. Exemple:
  
 <code lisp> <code lisp>
Line 167: Line 203:
  
 </​code>​ </​code>​
-Racket permite forme prescurtate pentru compuneri de funcții de tip //car// și //cdr//. Rescriem exemplele de mai sus folosind aceste forme prescurtate:​+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> <code lisp>
Line 191: Line 227:
 ===== Legarea variabilelor ===== ===== 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).+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> <code lisp>
Line 202: Line 238:
 ===== Funcții anonime (lambda) ===== ===== 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)%%''​.+O funcție anonimă se definește utilizând cuvântul cheie ''​%%lambda%%''​. Sintaxa este: ''​%%(lambda (arg1 arg2 ...) ce_întoarce_funcția)%%''​.
  
 <code lisp> <code lisp>
Line 232: Line 268:
  
 </​code>​ </​code>​
-Putem oricând scrie //λ// în loc de //lambda// (folosind ​//Ctrl+//).+Putem oricând scrie ''​%%λ%%'' ​în loc de ''​%%lambda%%'' ​(folosind ​''​%%Ctrl+\%%''​).
  
 ===== Funcții utile ===== ===== Funcții utile =====
Line 283: Line 319:
   * programarea este de tip **wishful thinking**   * programarea este de tip **wishful thinking**
   * secvența de instrucțiuni devine **compunere de funcții** (secvență de aplicări de funcții)   * 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.+  * 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ă   * 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:+**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>​ <​code>​
Line 331: Line 367:
 ===== Resurse ===== ===== Resurse =====
  
-  * {{ intro-ex.zip |Exerciții}} +  * [[https://​ocw.cs.pub.ro/​courses/​_media/​pp/​21/​laboratoare/​racket/​intro-skel.zip|Schelet]] 
-  * {{ intro-sol.zip |Soluții}} +  * [[https://​ocw.cs.pub.ro/​courses/​_media/​pp/​21/​laboratoare/​racket/​intro-sol.zip|Soluții]] 
-  * {{ intro-cheatsheet.pdf |Cheatsheet}}+  * [[https://​github.com/​cs-pub-ro/​PP-laboratoare/​raw/​master/​racket/​intro/racket-cheatsheet-1.pdf|Cheatsheet]]
  
 ===== Referinţe ===== ===== Referinţe =====
Line 345: Line 381:
   * [[https://​en.wikipedia.org/​wiki/​Strong_typing|Tipare strong]]   * [[https://​en.wikipedia.org/​wiki/​Strong_typing|Tipare strong]]
   * [[https://​en.wikipedia.org/​wiki/​Eager_evaluation|Evaluare aplicativă]]   * [[https://​en.wikipedia.org/​wiki/​Eager_evaluation|Evaluare aplicativă]]
-  * [[https://​www.gnu.org/​software/​mit-scheme/​documentation/​mit-scheme-user/​Coding-style.html|Scheme coding style]]+  * [[https://​www.gnu.org/​software/​mit-scheme/​documentation/stable/​mit-scheme-user/​Coding-style.html|Scheme coding style]]
  
  
pp/21/laboratoare/racket/intro.1614524564.txt.gz · Last modified: 2021/02/28 17:02 by bot.pp
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