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.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

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)), ... ]

2. Define the select function which takes a tolerance $ e$ and a list $ l$ and returns the element $ l_n$ which satisfies the following condition: $ abs(l_n - l_{n+1}) < e$

Numerical Constants

Phi

We know that $ \displaystyle \lim_{n \rightarrow \infty} \frac{F_{n+1}}{F_n} = \varphi$ (where $ F_n$ is the n-th element of the Fibonacci sequence, and $ \varphi$ is "the Golden Ratio"). More info here.

3. Write an approximation with 0.001 tolerance for the $ \varphi$ constant. Use the previously defined Fibonacci stream.

Pi

Consider the sequence:

$ a_{n+1} = a_n + sin(a_n)$ ; where $ a_0$ is an initial approximation, randomly chosen (but not 0 because $ a_{n+1} != a_n$ ).

We know that $ \displaystyle \lim_{n \rightarrow \infty} a_n = \pi$

4. Write an approximation with 0.001 tolerance of the $ \pi$ constant.

Square Root

Given a number $ k$ , we want to find a numerical approximation for $ \sqrt{k}$ . Consider the sequence:

$ a_{n+1} = \frac{1}{2}(a_n + \frac{k}{a_n})$ ; where $ a_0$ is an initial approximation, randomly chosen.

We know that $ \displaystyle \lim_{n \rightarrow \infty} a_n = \sqrt{k}$ ; more info here.

5. Write a function that approximates $ \sqrt{k}$ with 0.001 tolerance.

The Newton-Raphson Method

The sequence used for the approximation of the square root can be derived from the Newton-Raphson method, a generic method for finding the roots of a function (i.e. the points $ x$ for which $ f(x) = 0$ ). Thus, for a function $ f$ , we have the sequence:

$ x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}$

We know that $ \displaystyle \lim_{n \rightarrow \infty} x_n = r\ a.î.\ f(r) = 0$ .

6. Write a function which takes a function and and its derivative and it approximates a root with 0.001 tolerance.

Derivatives

We can approximate the derivative of a function in a certain point using the definition of the derivative:

$ \displaystyle f'(a)=\lim_{h \rightarrow 0} \frac{f(a+h)-f(a)}{h}$

We can obtain better succesive approximations of the derivative in a point $ a$ , using a smaller $ h$ .

7. Write a function which approximates the derivative of a function in a certain point. follow the steps:

a) generate the sequence: $ h_0, \frac{h_0}{2}, \frac{h_0}{4}, \frac{h_0}{8}, \ldots$ (where $ h_0$ is a randomly chosen initial approximation)
b) generate the list of approximations for $ f'(a)$ , using the formula above
c) write the function that takes a function $ f$ and a point $ a$ and approximates $ f'(a)$ with 0.001 tolerance, using the previous steps.

Integrals (:

Given a function $ f$ , we can approximate the definite integral on $ [a, b]$, using the area of the trapezoid defined by $ a, b, f(a), f(b)$ :

$ \displaystyle \int_{a}^{b} f(x) dx \approx (b - a)\frac{f(a)+f(b)}{2}$

We can obtain a better approximation by dividing the interval in two and adding the area of the two trapezoids defined by $ a, m, f(a), f(m)$ and $ m, b, f(m), f(b)$ (where $ 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. Write a function which approximates the integral of a function on an interval. Follow the steps:

a) Write a function which takes a function $ f$ and two points $ a, b$ and calculates the area of the trapezoid $ a, b, f(a), f(b)$
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]

c) Write a function which takes a function $ f$ and a list of points $ p_0,\ p_1,\ p_2,\ p_3,\ \ldots$ and returns the list containing the areas of trapezoids defined by two consecutive points:

$ (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) Write a function which takes a function $ f$ and two points $ a, b$ and approximates $ \displaystyle \int_{a}^{b} f(x) dx$ with 0.001 tolerance, using the previous steps.