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 [2017/04/27 09:18]
pdmatei
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) === +3Scrieți ​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 o 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_n] din lista care satisface proprietatea:​ $math[abs(a_n - a_{n+1}) < e] +
-  - Aproximatie pentru $math[\sqrt{k}]:​ +
-      * Scrieti o 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. +
-  - Aproximatie pentru derivata unei functii ''​f''​ intr-un punct ''​a''​ +
-      * Scrieti o functie care genereaza lista: ''​[h0,​ h0/2, h0/4, h0/8, ...]''​ +
-      * Scrieti o functie care calculeaza lista aproximarilor lui ''​f'​(a)'',​ calculate astfel: ​$math[f'(a)=\lim_{h \rightarrow 0} \frac{f(a+h)-f(a)}{h}] +
-      * Scrieti o functie care calculeaza derivata in ''​a''​ a unei functii ''​f'',​ cu toleranta ''​0.01''​ +
-  ​Aproximatie pentru integrala unei functii pe intervalul ''​[a,​b]''​ +
-      * Scrieti o 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.+
  
-=== Solutii ​=== +== pi == 
-[[https://github.com/Programming-Paradigms/Labs/archive/​master.zip|Solutii ​(inclusiv ​pentru ​laboratorul 9)]]+ 
 +Fie șirul: 
 + 
 +$math[a_{n+1} ​a_n + sin(a_n)]; unde $math[a_0] este o //​aproximare inițială//,​ aleasă arbitrar (dar diferită de 0 pentru ca $math[a_{n+1} !a_n]). 
 + 
 +Știm că $math[\displaystyle \lim_{n \rightarrow \infty} a_n = \pi] 
 + 
 +4. Scrieți o 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 o 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 o 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 \infty} x_n = r\ a.î.\ f(r) = 0]. 
 + 
 +6. Scrieți o 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 o altă funcție (implementată manual) care 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 o 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 o 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: 
 + 
 +''​[1,​ 4, 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)]]