====== 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.)**
foldr ((||).(==1)) False [2,1,3,4,5,6,7]
**b.)**
foldl (flip ((||).(==1))) False [2,1,3,4,5,6,7]
===== 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.
nats :: [Integer]
**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.
-- 1, 3, 5, 7, ...
odds :: [Integer]
-- 0, 1, 4, 9, ...
squares :: [Integer]
**8.2.3.** Define the stream of **Fibonacci** numbers. Hint: '':t zipWith''.
fibs :: [Integer]
===== 8.3. Infinite Binary Trees =====
Defining a simple **binary tree** structure in //Haskell// is easy.\\
Take this for example:
data BTree = Node Int BTree BTree | Nil deriving Show
{-
1
|
----------
| |
2 3
------ ------
| | | |
Nil Nil Nil Nil
-}
tree :: BTree
tree = Node 1 (Node 2 Nil Nil) (Node 3 Nil Nil)
But what if we want to enforce an **infinite binary tree**?\\
The solution is eliminating the need for the empty node ''Nil'':
-- This is an infinite data type, no way to stop generating the tree
data StreamBTree = StreamNode Int StreamBTree StreamBTree
**8.3.1.** Define the ''repeatTree'' function which takes an **Int** ''k'' and generates an **infinite tree**, where each node has the value ''k''.
{-
> repeatTree 3
3
|
---------
| |
3 3
------ ------
| | | |
3 3 3 3
. . . .
. . . .
-}
repeatTree :: Int -> StreamBTree
**8.3.2.** In order to view an **infinite tree**, we need to convert it to a **finite binary tree**. Define the function ''sliceTree'', which takes a level ''k'', an **infinite tree** and returns the first ''k'' levels of our tree, in the form of a **finite** one.
{-
> sliceTree 2 (repeatTree 3)
3
|
----------
| |
3 3
------ ------
| | | |
Nil Nil Nil Nil
-}
sliceTree :: Int -> StreamBTree -> BTree
**8.3.3.** Define the ''generateTree'' function, which takes a **root** ''k'', a **left generator** function ''leftF'' and a **right generator** function ''rightF''.\\
For example, let's say we have ''k=2'', ''leftF=(+1)'', ''rightF=(*2)''. This should generate a tree where the //root// is $math[2], the //left child// is $math[parent + 1]\\
and the //right child// is $math[parent * 2].
{-
> generateTree 2 (+1) (*2)
2
|
-------------------------------------------
| |
3 4
----------------- -----------------
| | | |
4 6 5 8
--------- --------- --------- ---------
| | | | | | | |
5 8 7 12 6 10 9 16
----- ----- ----- ----- ----- ----- ----- -----
| | | | | | | | | | | | | | | |
6 10 9 16 8 14 13 24 7 12 11 20 10 18 17 32
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
-}
generateTree :: Int -> (Int -> Int) -> (Int -> Int) -> StreamBTree
===== 8.4. Numerical Approximations =====
**8.4.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)), .%%.%%. ]''.
{-
with this function, you should
be able to easily define the natural numbers
with something like: build (+1) 0
-}
build :: (Double -> Double) -> Double -> [Double]
**8.4.2.** Using the ''build'' function, define the following **streams**:
-- 0, 1, 0, 1, 0, 1, 0, ...
alternatingBinary :: [Double]
-- 0, -1, 2, -3, 4, -5, ...
alternatingCons :: [Double]
-- 1, -2, 4, -8, 16, ...
alternatingPowers :: [Double]
**8.4.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].
select :: Double -> [Double] -> Double
==== Mathematical Constants ====
**8.4.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.
phiApprox :: Double
**8.4.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''.
piApprox :: Double
==== Square Root ====
**8.4.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''.
sqrtApprox :: Double -> Double
==== Derivatives ====
**8.4.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.
derivativeApprox :: (Double -> Double) -> Double -> Double
===== 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)]]