Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
pp:lazylab [2021/04/25 14:59] pdmatei |
pp:lazylab [2021/04/25 17:20] (current) calin_andrei.bucur |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== 8. Lazy evaluation ====== | ====== 8. Lazy evaluation ====== | ||
- | ====== Evaluare leneșă ====== | + | When passing **parameters** to a function, programming language design offers **two options** which are not mutually exclusive (both strategies can be implemented in the language): |
+ | * **applicative** (also called **strict**) evaluation strategy: | ||
+ | * parameters are always evaluated **first** | ||
+ | * can be further refined into: **call-by-value** (e.g. as it happens in C) and **call-by-reference** (e.g. as it happens for objects in Java). | ||
+ | * **normal** evaluation strategy (also called **non-strict**, and when implemented as part of the PL - **call-by-name**) | ||
+ | * the function is always evaluated **first** | ||
+ | * can be further refined into: **lazy**, which ensures that each expression is evaluated **at most once** | ||
- | =====Moduri de evaluare a unei expresii: ===== | + | For more details, see the lecture on lazy evaluation. In Haskell, the default evaluation strategy is **lazy**. However, there are ways in which we can force evaluation to be **strict**. In this lab, we will explore several programming constructs which benefit from lazy evaluation. |
- | * **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 aplicativă vs. evaluare normală==== | ||
- | 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): | + | ===== 8.1. Streams ===== |
- | <code>(λx.λy.(x + y) 1 2)</code> | + | 8.1.1. Define the stream of natural numbers ''nat :: [Integer]''\\ |
+ | 8.1.2. Define the stream of odd numbers. You can use other higher-order functions such as filter, map or zipWith.\\ | ||
+ | 8.1.3. Define the stream of Fibonacci numbers | ||
- | Evident, în urma aplicării funcției de mai sus, expresia se va evalua la 3. | + | ===== 8.2. Numerical approximations ===== |
- | 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> | + | |
- | 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. | + | 1. Define the ''build'' function which takes a generator ''g'' and an initial value ''a0'' and generates the infinite list: ''[a0, g a0, g (g a0), g (g (g a0)), .%%.%%. ]'' |
- | ====Evaluare aplicativă==== | + | 2. Define the ''select'' function which takes a tolerance $math[e] and a list $math[l] and returns the element $math[l_n] which satisfies the following condition: $math[abs(l_n - l_{n+1}) < e] |
- | Evaluarea aplicativă (eager evaluation) este cea în care fiecare expresie este evaluată imediat. În exemplul de mai sus, evaluarea aplicativă va decurge astfel: | + | === Numerical Constants === |
- | <code>(λx.λy.(x + y) 1 (λz.(z + 2) 3)) | + | == Phi == |
- | (λx.λy.(x + y) 1 5) | + | |
- | 6</code> | + | |
+ | We know that $math[\displaystyle \lim_{n \rightarrow \infty} \frac{F_{n+1}}{F_n} = \varphi] (where $math[F_n] is the n-th element of the Fibonacci sequence, and $math[\varphi] is [[https://en.wikipedia.org/wiki/Golden_ratio|"the Golden Ratio"]]). More info [[https://en.wikipedia.org/wiki/Golden_ratio#Relationship_to_Fibonacci_sequence|here]]. | ||
- | ====Evaluare normală==== | + | 3. Write an approximation with ''0.001'' tolerance for the $math[\varphi] constant. Use the previously defined Fibonacci stream. |
- | Spre deosebire de evaluarea aplicativă, evaluarea normală va întarzia evaluarea unei expresii, până când aceasta este folosită efectiv. Exemplu: | + | == Pi == |
- | <code>(λx.λy.(x + y) 1 (λz.(z + 2) 3)) | + | Consider the sequence: |
- | (1 + (λz.(z + 2) 3)) | + | |
- | (1 + 5) | + | |
- | 6</code> | + | |
+ | $math[a_{n+1} = a_n + sin(a_n)]; where $math[a_0] is an //initial approximation//, randomly chosen (but not 0 because $math[a_{n+1} != a_n]). | ||
- | ===== Exerciții: ===== | + | We know that $math[\displaystyle \lim_{n \rightarrow \infty} a_n = \pi] |
- | {{:pp:lazy.zip|Laborator 8 - Schelet}}\\ | + | 4. Write an approximation with ''0.001'' tolerance of the $math[\pi] constant. |
- | ==== I. Streams ==== | + | === Square Root === |
- | 1. construiți șirul numerelor naturale\\ | + | Given a number $math[k], we want to find a numerical approximation for $math[\sqrt{k}]. Consider the sequence: |
- | 2. construiți șirul numerelor pare (puteți să va folosiți de șirul definit anterior)\\ | + | |
- | 3. construiți șirul Fibonacci (în testele automate, șirul începe cu ''1, 1, 2, 3, 5, 8, .%%.%%.'') | + | |
- | ==== II. Aproximații numerice ==== | + | $math[a_{n+1} = \frac{1}{2}(a_n + \frac{k}{a_n})]; where $math[a_0] is an //initial approximation//, randomly chosen. |
- | 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)), .%%.%%. ]'' | + | We know that $math[\displaystyle \lim_{n \rightarrow \infty} a_n = \sqrt{k}]; more info [[https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method | here]]. |
- | 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] | + | 5. Write a function that approximates $math[\sqrt{k}] with ''0.001'' tolerance. |
- | === Constante numerice === | + | === The Newton-Raphson Method === |
- | == phi == | + | The sequence used for the approximation of the square root can be derived from the [[https://en.wikipedia.org/wiki/Newton%27s_method|Newton-Raphson method]], a generic method for finding the roots of a function (i.e. the points $math[x] for which $math[f(x) = 0]). Thus, for a function $math[f], we have the sequence: |
- | + | ||
- | Ș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]]. | + | |
- | + | ||
- | 3. Scrieți o aproximare cu toleranță ''0.001'' a constantei $math[\varphi]. Folosiți-vă de stream-ul Fibonacci definit anterior. | + | |
- | + | ||
- | == pi == | + | |
- | + | ||
- | 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)}] | $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]. | + | We know that $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''. | + | 6. Write a function which takes a function and and its derivative and it approximates a root with ''0.001'' tolerance. |
- | === Derivate === | + | === Derivatives === |
- | 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: | + | We can approximate the derivative of a function in a certain point using the definition of the derivative: |
$math[\displaystyle f'(a)=\lim_{h \rightarrow 0} \frac{f(a+h)-f(a)}{h}] | $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. | + | We can obtain better succesive approximations of the derivative in a point $math[a], using a smaller $math[h]. |
- | 7. Scrieți o funcție care să aproximeze derivata unei funcții într-un punct. Urmăriți pașii: | + | 7. Write a function which approximates the derivative of a function in a certain point. follow the steps: |
- | 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)\\ | + | a) generate the sequence: $math[h_0, \frac{h_0}{2}, \frac{h_0}{4}, \frac{h_0}{8}, ...] (where $math[h_0] is a randomly chosen //initial approximation//)\\ |
- | b) generați lista aproximarilor lui $math[f'(a)], folosind formula de mai sus\\ | + | b) generate the list of approximations for $math[f'(a)], using the formula above\\ |
- | 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. | + | c) write the function that takes a function $math[f] and a point $math[a] and approximates $math[f'(a)] with ''0.001'' tolerance, using the previous steps. |
- | === Integrale === | + | === Integrals (: === |
- | 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)]: | + | Given a function $math[f], we can approximate the definite integral on $ [a, b]$, using the area of the trapezoid defined by $math[a, b, f(a), f(b)]: |
$math[\displaystyle \int_{a}^{b} f(x) dx \approx (b - a)\frac{f(a)+f(b)}{2}] | $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. | + | We can obtain a better approximation by dividing the interval in two and adding the area of the two trapezoids defined by $math[a, m, f(a), f(m)] and $math[m, b, f(m), f(b)] (where $math[m] is the middle of the interval $ [a, b]$). We can obtain a better approximation by dividing these intervals in two and so on. |
- | 8. Scrieți o funcție care să aproximeze integrala unei funcții pe un interval. Urmăriți pașii: | + | 8. Write a function which approximates the integral of a function on an interval. Follow the steps: |
- | 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)]\\ | + | a) Write a function which takes a function $math[f] and two points $math[a, b] and calculates the area of the trapezoid $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: | + | b) Write a function which takes a (ascending) list of points and inserts between any two points their middle: |
''[1, 4, 7, 10, 13] -%%>%% [1, 2.5, 4, 5.5, 7, 8.5, 10, 11.5, 13]'' | ''[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:\\ | + | c) Write a function which takes a function $math[f] and a list of points $math[p_0,\ p_1,\ p_2,\ p_3,\ ...] and returns the list containing the areas of trapezoids defined by two consecutive points:\\ |
$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));\ ...] | $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. | + | d) Write a function which takes a function $math[f] and two points $math[a, b] and approximates $math[\displaystyle \int_{a}^{b} f(x) dx] with ''0.001'' tolerance, using the previous steps. |
===== Recommended Reading ===== | ===== 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)]] | + | * [[http://worrydream.com/refs/Hughes-WhyFunctionalProgrammingMatters.pdf| Why Functional Programming Matters (especially section 4 "Gluing Programs Together", where the lab exercises are inspired from)]] |