This is an old revision of the document!
8. Lazy evaluation
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
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.
8.1. Streams
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
8.2. Numerical approximations
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)), ... ]
2. Definiți funcția select
care primește o toleranță $ e$ și o listă $ l$ și întoarce elementul $ l_n$ care satisface proprietatea: $ abs(l_n - l_{n+1}) < e$
Constante numerice
phi
Știm că $ \displaystyle \lim_{n \rightarrow \infty} \frac{F_{n+1}}{F_n} = \varphi$ (unde $ F_n$ este al n-lea element din șirul lui Fibonacci, iar $ \varphi$ este "proporția de aur"). Mai multe informații aici.
3. Scrieți o aproximare cu toleranță 0.001
a constantei $ \varphi$ . Folosiți-vă de stream-ul Fibonacci definit anterior.
pi
Fie șirul:
$ a_{n+1} = a_n + sin(a_n)$ ; unde $ a_0$ este o aproximare inițială, aleasă arbitrar (dar diferită de 0 pentru ca $ a_{n+1} != a_n$ ).
Știm că $ \displaystyle \lim_{n \rightarrow \infty} a_n = \pi$
4. Scrieți o aproximare cu toleranță 0.001
a constantei $ \pi$ .
Rădăcină pătrată
Fiind dat un număr $ k$ , vrem să găsim o aproximare numerică pentru $ \sqrt{k}$ . Fie șirul:
$ a_{n+1} = \frac{1}{2}(a_n + \frac{k}{a_n})$ ; unde $ a_0$ este o aproximare inițială, aleasă arbitrar.
Știm că $ \displaystyle \lim_{n \rightarrow \infty} a_n = \sqrt{k}$ ; mai multe informații găsiți aici.
5. Scrieți o funcție care aproximează $ \sqrt{k}$ cu toleranța 0.001
.
Metoda Newton-Raphson
Șirul folosit pentru aproximarea rădăcinii pătrate se poate deriva din metoda Newton-Raphson, o metodă generică pentru a găsi rădăcinile unei funcții (i.e. punctele $ x$ pentru care $ f(x) = 0$ ). Astfel pentru o funcție $ f$ , avem șirul:
$ x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}$
Știm că $ \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 $ f$ . Dacă nu avem o astfel de implementare, putem aproxima derivata unei funcții într-un anumit punct. Folosindu-ne de definiția derivatei:
$ \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 $ a$ , folosind un $ 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: $ h_0, \frac{h_0}{2}, \frac{h_0}{4}, \frac{h_0}{8}, \ldots$ (unde $ h_0$ este o aproximare inițială, aleasă arbitrar)
b) generați lista aproximarilor lui $ f'(a)$ , folosind formula de mai sus
c) scrieți funcția care primește o funcție $ f$ și un punct $ a$ și aproximează $ f'(a)$ cu toleranța 0.001
, folosindu-vă de subpunctele anterioare.
Integrale
Dându-se o funcție $ f$ , putem aproxima integrala definită pe intervalul $ [a, b]$, folosind aria trapezului definit de $ a, b, f(a), f(b)$ :
$ \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 $ a, m, f(a), f(m)$ și de $ m, b, f(m), f(b)$ (unde $ 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 $ f$ și două puncte $ a, b$ și calculează aria trapezului $ 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 $ f$ și o listă de puncte $ p_0,\ p_1,\ p_2,\ p_3,\ \ldots$ și întoarce lista ariilor trapezelor descrise de două puncte consecutive:
$ (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));\ \ldots$
d) Scrieți o funcție care primește o funcție $ f$ și două puncte $ a, b$ și aproximează $ \displaystyle \int_{a}^{b} f(x) dx$ cu toleranța 0.001
, folosindu-vă de subpunctele anterioare.