Differences
This shows you the differences between two versions of the page.
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 observam, insa, 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 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 o 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)]] |