Edit this page Backlinks This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ====== 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. Evaluation ===== **8.1.1.** Describe the **evaluation strategy** of the following expressions: **a.)** <code haskell> foldr ((||).(==1)) False [2,1,3,4,5,6,7] </code> **b.)** <code haskell> foldl (flip ((||).(==1))) False [2,1,3,4,5,6,7] </code> ===== 8.2. Streams ===== For this lab, **streams** are synonymous with **infinite** lists.\\ If you want to see their content, use the ''take'' function ('':t take''). **8.2.1.** Define the stream of **natural** numbers. <code haskell> nats :: [Integer] </code> **8.2.2.** Using the stream of **natural** numbers, define the stream of **odd** numbers and **perfect squares**. Hint: use //higher order functions// for a quick solution. <code haskell> -- 1, 3, 5, 7, ... odds :: [Integer] -- 0, 1, 4, 9, ... squares :: [Integer] </code> **8.2.3.** Define the stream of **Fibonacci** numbers. Hint: '':t zipWith''. <code haskell> fibs :: [Integer] ===== 8.3. Infinite Binary Trees ===== Defining a simple binary tree structure in //Haskell// is easy.\\ Take this for example: <code haskell> data BTree = Node Int BTree BTree | Nil {- 1 | ---------- | | 2 3 ------ ------ | | | | Nil Nil Nil Nil -} tree :: BTree tree = Node 1 (Node 2 Nil Nil) (Node 3 Nil Nil) </code> ===== 8.3. Numerical Approximations ===== **8.3.1.** Define the ''build'' function which takes a generator ''g'' and an initial value ''a0'' and generates the **stream**: ''[a0, g a0, g (g a0), g (g (g a0)), .%%.%%. ]''. <code haskell> {- with this function, you should be able to easily define the natural numbers with something like: build (+1) 0 -} build :: (Double -> Double) -> Double -> [Double] </code> **8.3.2.** Using the ''build'' function, define the following **streams**: <code haskell> -- 0, 1, 0, 1, 0, 1, 0, ... alternatingBinary :: [Double] -- 0, -1, 2, -3, 4, -5, ... alternatingCons :: [Double] -- 1, -2, 4, -8, 16, ... alternatingPowers :: [Double] </code> **8.3.3.** Define the ''select'' function which takes a **tolerance** ''e'' and a **stream** ''s'' and returns the ''nth'' element of the **stream** which satisfies the following condition: $math[\lvert s_n - s_{n+1} \rvert < e]. <code haskell> select :: Double -> [Double] -> Double </code> ==== Mathematical Constants ==== **8.3.4.** Knowing that $math[\displaystyle \lim_{n \rightarrow \infty} \frac{F_{n+1}}{F_n} = \varphi], where $math[F_n] is the nth element of the **Fibonacci** sequence, write an\\ approximation with **tolerance** ''e=0.00001'' of the **Golden Ration** ($math[\varphi]). Use the previously defined ''fibs'' **stream** and the ''select'' function. <code haskell> phiApprox :: Double </code> **8.3.5.** Consider the //sequence//: $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]). Knowing that $math[\displaystyle \lim_{n \rightarrow \infty} a_n = \pi], write an approximation with **tolerance** ''e=0.00001'' of **$math[\pi]**. Make sure to use ''build'' and ''select''. <code haskell> piApprox :: Double </code> ==== Square Root ==== **8.3.6.** Given a number ''k'', we want to create a function which calculates the\\ **square root** of ''k''. This is another place where **laziness** and **streams** come into play. Consider the following //sequence//: $math[a_{n+1} = \frac{1}{2}(a_n + \frac{k}{a_n})]; where $math[a_0] is an //initial approximation//, randomly chosen. Knowing that $math[\displaystyle \lim_{n \rightarrow \infty} a_n = \sqrt{k}], write a function that approximates $math[\sqrt{k}] with **tolerance** ''e=0.00001''. Use ''build'' and ''select''. <code haskell> sqrtApprox :: Double -> Double </code> ==== Derivatives ==== **8.3.7.** 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}] We can obtain better successive approximations of the derivative in a point $math[a], using a smaller $math[h]. **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)** generate the list of approximations for $math[f'(a)], using the formula above\\ **c)** write the function that takes a function $math[f] and a point $math[a] and approximates $math[f'(a)] with tolerance ''e=0.00001'', using the previous steps. <code haskell> derivativeApprox :: (Double -> Double) -> Double -> Double </code> ==== The Newton-Raphson Method ==== **8.3.8.** The method used for approximating the square root is derived from a more general one, named **The Newton-Raphson Method**. This is a generic method used for finding the **roots** of a function ($math[x] | $math[f(x) = 0]). Considering the following //sequence//: $math[x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}] and the //limit//: $math[\displaystyle \lim_{n \rightarrow \infty} x_n = r\ \Rightarrow \ f(r) = 0] write a function which takes another ''function'' and it approximates a root with **tolerance** ''e=0.00001''. Make use of ''build'', ''select'' and ''derivativeApprox''. <code haskell> rootApprox :: (Double -> Double) -> Double </code> ==== Integrals (:< ==== **8.3.9.** 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}] 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. - Write a function which approximates the integral of a function on an interval. Follow these **steps**: **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)]\\ <code haskell> trapezoidArea :: (Double -> Double) -> Double -> Double -> Double </code> **b)** Write a function which takes an ascending list of points and inserts between any two points their middle: <code haskell> -- [1, 4, 7, 10, 13] ->[1, 2.5, 4, 5.5, 7, 8.5, 10, 11.5, 13] insertMiddle :: [Double] -> [Double] </code> **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:\\ <code haskell> -- f -> [p0, p1, p2, p3, ...] -> [area p0 p1 f(p0) f(p1), area p1 p2 f(p1) f(p2), area p2 p3 f(p2) f(p3)] consArea :: (Double -> Double) -> [Double] -> [Double] </code> **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 **tolerance** ''e=0.00001'', using the previous steps. <code haskell> integralApprox :: (Double -> Double) -> Double -> Double -> Double </code> ===== Recommended Reading ===== * [[http://worrydream.com/refs/Hughes-WhyFunctionalProgrammingMatters.pdf| Why Functional Programming Matters (especially section 4 "Gluing Programs Together", where the lab exercises are inspired from)]]