Differences

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

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
pp:l06 [2018/02/25 14:41]
sergiu [Aplicatii:]
pp:l06 [2020/02/05 15:50] (current)
dmihai [Exerciții:]
Line 1: Line 1:
-===== Întârzierea evaluării =====+====== Evaluare leneșă ======
  
  
 =====Moduri de evaluare a unei expresii: ===== =====Moduri de evaluare a unei expresii: =====
-    * **evaluare ​aplicativa** - argumentele funcțiilor sunt evaluate înaintea aplicării funcției asupra lor.+    * **evaluare ​aplicativă** - argumentele funcțiilor sunt evaluate înaintea aplicării funcției asupra lor.
     * **evaluare lenesa** - întârzie evaluarea parametrilor până în momentul când aceasta este folosită efectiv.     * **evaluare lenesa** - întârzie evaluarea parametrilor până în momentul când aceasta este folosită efectiv.
-    * **promisiuni** 
  
-====Evaluare aplicativă vs. evaluare ​normala====+====Evaluare aplicativă vs. evaluare ​normală====
  
-Fie urmatoarea expresie, scrisă într-o ​varianta relaxata ​a Calculului Lambda (in care valori numerice ​si operatii ​pe acestea sunt permise, iar functiile ​sunt in forma uncurry):+Fie urmatoarea expresie, scrisă într-o ​variantă relaxată ​a Calculului Lambda (în care valori numerice ​și operații ​pe acestea sunt permise, iar funcțiile ​sunt în formă uncurried):
  
 <​code>​(λx.λy.(x + y) 1 2)</​code>​ <​code>​(λx.λy.(x + y) 1 2)</​code>​
  
-Evident, ​in urma aplicării funcției de mai sus, expresia se va evalua la 3. +Evident, ​în urma aplicării funcției de mai sus, expresia se va evalua la 3. 
-Sa observaminsa, cazul in care parametrii ​functiei reprezinta aplicatii ​de functii:+Să observămînsă, cazul în care parametrii ​funcției reprezintă aplicații ​de funcții:
    
 <​code>​(λx.λy.(x + y) 1 (λz.(z + 2) 3))</​code>​ <​code>​(λx.λy.(x + y) 1 (λz.(z + 2) 3))</​code>​
  
-Desigur, evaluarea expresiei ''​(λz.(z + 2) 3)''​ va genera valoarea ''​5'',​ de unde deducem ​ca rezultatul final al expresiei va fi ''​6''​ ( adunarea lui 1 cu rezultatul anterior ). În cadrul acestui raționament,​ am presupus că parametrii sunt evaluați înaintea aplicării funcției asupra acestora. Vom vedea, în cele ce urmează, că evaluarea se poate realiza și in alt mod.+Desigur, evaluarea expresiei ''​(λz.(z + 2) 3)''​ va genera valoarea ''​5'',​ de unde deducem ​că rezultatul final al expresiei va fi ''​6''​ (adunarea lui 1 cu rezultatul anterior). În cadrul acestui raționament,​ am presupus că parametrii sunt evaluați înaintea aplicării funcției asupra acestora. Vom vedea, în cele ce urmează, că evaluarea se poate realiza și in alt mod.
  
 ====Evaluare aplicativă==== ====Evaluare aplicativă====
Line 38: Line 37:
 6</​code>​ 6</​code>​
  
-====Evaluarea in Racket==== 
  
-Evaluarea in Racket este by default **aplicativă**. +===== Exerciții: =====
-Codul ''​(+ 1 (+ 2 3))''​ pentru a se evalua si a intoarce 6, urmează etapele: +
-  * ''​(+ 2 3)''​ se evaluează intai, al doilea parmetru al funcției ''​+''​ si produce 5 +
-  * ''​(+ 1 5)''​ se evaluează si produce 6+
  
-Totuși, putem intarzia evaluarea expresiilor prin inchideri nulare+{{:​pp:​lazy.zip|Laborator 8 - Schelet}}\\
  
-<​code>​(define suma +==== I. Streams ====
-  (lambda (a b) +
-    (+ a b)))+
  
-(define suma_intarziata +1. construiți șirul numerelor naturale\\ 
-  (lambda (a b+2. construiți șirul numerelor pare (puteți să va folosiți de șirul definit anterior)\\ 
-    (lambda () (+ a b) +3. construiți șirul Fibonacci ​(în testele automate, șirul începe cu ''​1,​ 1, 2, 3, 5, 8, .%%.%%.''​)
-    )))</​code>​+
  
-  * ''​(suma_intarziata 2 3)''​ intoarce un ''#​procedure''​ (functie) care isi salveaza contextul ( inchidere functionala ) +==== II. Aproximații numerice ====
-  * ''​(''''​(suma_intarziata 2 3)''''​)''​ intoarce rezultatul+
  
 +1. Definiți funcția ''​build''​ care primește un generator ''​g''​ și o valoare inițială ''​a0''​ și generează lista infinită: ''​[a0,​ g a0, g (g a0), g (g (g a0)), .%%.%%. ]''​
  
-====Aplicatii:====+2. Definiți funcția ''​select''​ care primește o toleranță $math[e] și o listă $math[l] și întoarce elementul $math[l_n] care satisface proprietatea$math[abs(l_n - l_{n+1}) < e]
  
-=== Siruri in Scheme ​===+=== Constante numerice ​===
  
-  - Definiți un stream de numere 1 '​ones_stream'​ folosind evaluarea normala din Scheme +== phi ==
-  - Creati o funcție take care să funcționeze ca cea din Haskell (take 5 [1,1..] va intoarce [1,1,1,1,1] in haskell) +
-      * ''​(take 5 ones_stream)'' ​=> ''​(1 1 1 1 1)''​ +
-  - Creati un stream de numere naturale care va fi reprezentat astfel: +
-      * ''​(0 . #​procedure)'',​ unde dacă vom apela procedure vom obtine +
-      * ''​(1 . #​procedure)'',​ unde dacă iar apelam procedure-ul nou obtinut, obtinem +
-      * ''​(2 . #​procedure)''​ etc. +
-      * HINT: veți avea nevoie de o funcție ajutătoare +
-      * Testați: ''​(take 5 naturals_stream)'' ​=> ''​(0 1 2 3 4)''​ +
-  - Definiți o funcție care sa adune două stream-uri:​ +
-      * ''​(take 5 (sum_stream naturals_stream ones_stream))'' ​=> ''​(1 2 3 4 5)''​ +
-      * pentru acest task, implementati o functie ''​zipWith''​ care sa opereze pe stream-uri +
-  - Creați un stream de numere pare: +
-      * Testati: ''​(take 5 even_stream)'' ​=> ''​(0 2 4 6 8)''​ +
-      * pentru acest task, implementati o functie ''​map''​ care sa opereze pe stream-uri +
-  - Creați șirul numerelor Fibonacci:​ +
-      * ''​(take 5 fibo_stream)''​ => ''​(0 1 1 2 3)''​ +
-      * Observam ca fibonacci e definit prin adunarea stream-urilor anterioare. +
-              <​code>​ +
-              Fibo        = t0 t1 t2 t3 ... t(k-2) ... + +
-              (tail Fibo) = t1 t2 t3 t4 ... t(k-1) ... +
-              ___________________________________________ +
-              Fibo  = t0 t1 t2 t3 t4 t5 ... tk ...  </​code>​ +
-      * Observam că, adunand elemente din sirurile (fluxurile) Fibo și (tail Fibo), obtinem sirul t2 t3 t4 .. tk . Dacă adăugam primele două elemente, t0 si t1 la inceput, obținem exact Fibo.+
  
-=== Siruri in Haskell === +Știm că $math[\displaystyle \lim_{n \rightarrow \infty} \frac{F_{n+1}}{F_n} ​\varphi] (unde $math[F_n] este al n-lea element din șirul ​lui Fibonacci, iar $math[\varphi] este [[https://​en.wikipedia.org/​wiki/​Golden_ratio|"​proporția de aur"​]]). Mai multe informații [[https://​en.wikipedia.org/​wiki/​Golden_ratio#​Relationship_to_Fibonacci_sequence|aici]].
-  - Construiti sirul numerelor naturale +
-  - Construiti sirul numerelor pare  +
-  ​Construiti sirul numerelor ​lui Fibonacci ​+
  
-=== Aproximatii (in Haskell) === +3. Scrieți o aproximare cu toleranță ''​0.001''​ a constantei $math[\varphi]. Folosiți-vă de stream-ul Fibonacci definit anterior. 
-  - definiti o functie ''​build :: (a -> a-> a -> [a]''​ care primeste ​functie '​generator'​ (pe care o numim ''​g''​ in continuare)o valoare initiala ​(''​a0''​),​ si generaza lista: ''​[a0, g a0, g (g a0), g (g (g a0)), ... ''​ + 
-  - definiti ​functie ​''​select''​ care primeste o toleranta ''​e'',​ o lista, si intoarce valoarea ​$math[a_ndin lista care satisface proprietatea: $math[abs(a_n - a_{n+1}) ​< e+== pi == 
-  - Aproximatie pentru ​$math[\sqrt{k}]:​ + 
-      * Scrieti ​functie ​care calculeaza ​$math[\sqrt{k}] cu toleranta ​''​0.01''​, exploatand faptul ca sirul: $math[a_n = 1/2(a_{n-1} + k/a_{n-1})] converge catre $math[\sqrt{k}] cand ''​n''​ tine de la infinit+Fie șirul: 
-  - Aproximatie pentru derivata unei functii ''​f''​ intr-un punct ''​a''​ + 
-      * Scrieti ​functie ​care genereaza lista: ​''​[h0, h0/2, h0/4, h0/8, ...]''​ +$math[a_{n+1} = a_n + sin(a_n)]; unde $math[a_0este //​aproximare inițială//​aleasă arbitrar ​(dar diferită de 0 pentru ca $math[a_{n+1} != a_n]). 
-      * Scrieti ​functie care calculeaza lista aproximarilor lui ''​f'​(a)'',​ calculate ​astfel: $math[\displaystyle f'​(a)=\lim_{h \rightarrow 0} \frac{f(a+h)-f(a)}{h}] + 
-      * Scrieti ​functie ​care calculeaza ​derivata ​in ''​a'' ​unei functii ''​f'', ​cu toleranta ​''​0.01''​ +Știm că $math[\displaystyle \lim_{n \rightarrow \infty} a_n = \pi] 
-  Aproximatie pentru ​integrala ​unei functii ​pe intervalul ​''​[a,b]''​ + 
-      * Scrieti ​functie care aproximeaza valoarea integralei unei functii ''​f''​ intre ''​a''​ si ''​b''​cu toleranta ''​0.01''​. Strategia de imbunatatire ​a unei aproximari consta in spargerea intervalului ''​[a,b]''​ in doua sub-intervale ​de dimensiune egala ''​[a,m]'' ​si ''​[m,​b]'', ​calculul integralei pe fiecare, si adunarea rezultatului.+4. Scrieți ​aproximare cu toleranță ​''​0.001'' ​a constantei $math[\pi]. 
 + 
 +=== Rădăcină pătrată === 
 + 
 +Fiind dat un număr $math[k]vrem să găsim ​aproximare numerică pentru ​$math[\sqrt{k}]. Fie șirul: 
 + 
 +$math[a_{n+1} = \frac{1}{2}(a_n + \frac{k}{a_n})]; unde $math[a_0] este o //​aproximare inițială//,​ aleasă arbitrar. 
 + 
 +Știm că $math[\displaystyle \lim_{n \rightarrow \infty} a_n = \sqrt{k}]; mai multe informații găsiți [[https://​en.wikipedia.org/​wiki/​Methods_of_computing_square_roots#​Babylonian_method | aici]]. 
 + 
 +5. Scrieți ​funcție ​care aproximează ​$math[\sqrt{k}] cu toleranța ​''​0.001''​
 + 
 +=== Metoda Newton-Raphson === 
 + 
 +Șirul folosit pentru aproximarea rădăcinii pătrate se poate deriva din [[https://​en.wikipedia.org/​wiki/​Newton%27s_method|metoda Newton-Raphson]],​ o metodă generică pentru a găsi rădăcinile unei funcții (i.e. punctele ​$math[x] pentru care $math[f(x) = 0]). Astfel pentru o funcție $math[f], avem șirul: 
 + 
 +$math[x_{n+1} = x_n \frac{f(x_n)}{f'​(x_n)}] 
 + 
 +Știm că $math[\displaystyle \lim_{n \rightarrow \inftyx_n = r\ a.î.\ f(r) = 0]. 
 + 
 +6. Scrieți ​funcție ​care primește o funcție și derivata acesteia și aproximează o rădăcină cu toleranța ​''​0.001''​. 
 + 
 +=== Derivate === 
 + 
 +La exercițiul anterior, ne-am folosit de altă funcție ​(implementată manualcare să ne calculeze exact derivata funcției $math[f]. Dacă nu avem o astfel ​de implementare,​ putem aproxima derivata unei funcții într-un anumit punct. Folosindu-ne de definiția derivatei: 
 + 
 +$math[\displaystyle f'​(a)=\lim_{h \rightarrow 0} \frac{f(a+h)-f(a)}{h}] 
 + 
 +Putem obține aproximări succesive din ce în ce mai bune ale derivatei unei funcții într-un punct $math[a], folosind un $math[h] din ce în ce mai mic. 
 + 
 +7. Scrieți ​funcție ​care să aproximeze ​derivata ​unei funcții într-un punct. Urmăriți pașii: 
 + 
 +a) generați șirul: $math[h_0, \frac{h_0}{2},​ \frac{h_0}{4},​ \frac{h_0}{8},​ ...] (unde $math[h_0] este o //​aproximare inițială//,​ aleasă arbitrar)\\ 
 +b) generați lista aproximarilor lui $math[f'(a)], folosind formula de mai sus\\ 
 +c) scrieți funcția care primește o funcție $math[f] și un punct $math[a] și aproximează $math[f'(a)] cu toleranța ​''​0.001''​, folosindu-vă de subpunctele anterioare. 
 + 
 +=== Integrale === 
 + 
 +Dându-se o funcție $math[f], putem aproxima ​integrala ​definită ​pe intervalul ​[a, b]$, folosind aria trapezului definit de $math[a, b, f(a), f(b)]: 
 + 
 +$math[\displaystyle \int_{a}^{b} f(x) dx \approx (b - a)\frac{f(a)+f(b)}{2}] 
 + 
 +Putem obține ​aproximare mai bună împărțind intervalul în două și însumând aria celor două trapeze definite de $math[a, m, f(a), f(m)] și de $math[m, ​b, f(m), f(b)] (unde $math[m] este mijlocul intervalului $ [a, b]$)Putem obține o aproximare și mai bună împărțind aceste două intervale în două și tot așa. 
 + 
 +8. Scrieți o funcție care să aproximeze integrala ​unei funcții pe un interval. Urmăriți pașii: 
 + 
 +a) Scrieți o funcție care primește o funcție $math[f] și două puncte $math[a, b] și calculează aria trapezului $math[a, b, f(a), f(b)]\\ 
 +b) Scrieți o funcție care primește o listă (crescătoare) ​de puncte și inserează între oricare două elemente mijlocul acestor: 
 + 
 +''​[14, 7, 10, 13] -%%>%% [1, 2.5, 4, 5.5, 7, 8.5, 10, 11.5, 13]''​ 
 + 
 +c) Scrieți o funcție care primește o funcție $math[f] și o listă de puncte $math[p_0,\ p_1,\ p_2,\ p_3,\ ...] și întoarce lista ariilor trapezelor descrise de două puncte consecutive:​\\ 
 + 
 +$math[(p_0, p_1, f(p_0), f(p_1));\ (p_1, p_2, f(p_1), f(p_2));\ (p_2, p_3, f(p_2), f(p_3));\ ...] 
 + 
 +d) Scrieți o funcție care primește o funcție $math[f] și două puncte $math[a, b] și aproximează $math[\displaystyle \int_{a}^{b} f(x) dx] cu toleranța ''​0.001'', ​folosindu-vă de subpunctele anterioare. 
 + 
 +===== Recommended Reading ===== 
 + 
 +  * [[http://​worrydream.com/​refs/​Hughes-WhyFunctionalProgrammingMatters.pdf| Why Functional Programming Matters (în special secțiunea 4 "​Gluing Programs Together",​ de unde sunt inspirate exercițiile din laborator)]]