Topic 8: Lazy Evaluation 1
Recommended Exercises and Readings From Haskell: The craft of functional programming (3 rd Ed.) Exercises: 17.1, 17.2, 17.4, 17.8, 17.23, 17.25, 17.28, 17.29 Readings: Chapter 17.1, 17.2, 17.3, 17.6, 17.7 2
Lazy Evaluation When lazy evaluation is employed Objects and expressions are evaluated when they are needed Expressions that are not used subsequently are not evaluated Haskell is lazy In Haskell, lazy evaluation applies to Function parameters List elements Guards Logical expressions 3
Lazy Evaluation and Guards Consider the following function and function calls choice :: Int > a > a > a > a choice n x y z n > 0 = x n == 0 = y otherwise = z choice 10 (naivefib 29) (naivefib 30) (naivefib 31) choice 0 (naivefib 29) (naivefib 30) (naivefib 31) choice 2 (naivefib 29) (naivefib 30) (naivefib 31) 4
Lazy Evaluation and Pattern Matching Consider the following function and function calls multiply :: Int > Int > Int multiply 0 _ = 0 multiply _ 0 = 0 multiply x y = x * y multiply (12 + 12) (15 * 15) multiply 0 (naivefib 40) multiply (naivefib 40) 0 5
Lazy Evaluation With lazy evaluation Parameters are passed to the function without being evaluated Parameters are evaluated in the called scope only as required Repeated uses of the same parameter in the called scope are only evaluated once Composite parameters, such as lists and tuples, may only be partially evaluated 6
Evaluation in Java, C++ and Python In almost all circumstances, evaluation is eager Expressions are fully evaluated in the calling scope before they are passed to the function long nfib(long n) { if (n == 0) return 0; if (n == 1) return 1; return nfib(n 1) + nfib(n 2); } int main(int argc, char** argv) { cout << "Starting..." << endl; cout << " 0 * nfib(41) is " << multiply(0, nfib(41)) << endl; cout << " nfib(42) * 0 is " << multiply(nfib(42), 0) << endl; cout << " nfib(39) * nfib(40) is " << multiply(nfib(39), nfib(40)) << endl; long multiply(long x, long y) { if (x == 0) return 0; if (y == 0) return 0; return x * y; } return 0; } 7
Evaluation in Java, C++ and Python In C++, the optimizer can (sometimes) avoid fully evaluating expressions that are never used subsequently Mark nfib as a const function long nfib(long n) attribute ((const)); Turn on optimization g++ O lazy.cpp This is a rather different (and limited) form of lazy evaluation from what Haskell provides, but it provides some of the same benefits 8
Evaluation in Java, C++ and Python Exception to eager evaluation: Built in logical operators The and and or operators in Java, C++, C and Python all use a limited form of lazy evaluation that is often referred to as short circuit evaluation If the left operand s value conclusively determines the outcome of the logical expression then the right operand is not evaluated No matter what even if it has side effects! 9
Example: Prime Numbers How can we efficiently identify the first n prime numbers? Possible strategy: Start at two and check each number in sequence If the number is prime, add it to the list of primes Otherwise discard the number Repeat until n prime numbers have been identified What s undesirable about this solution? 10
Sieve of Eratosthenes An efficient technique for identifying all prime numbers from 2..n Starting from the beginning of the list Repeat Identify the next item in the list as prime Remove all multiples of the first element from consideration Until the next item is greater than or equal to n 11
Sieve of Eratosthenes 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 12
Sieve of Eratosthenes Implement the Sieve of Eratosthenes as a function Parameter: n (an integer), the maximum value to test Return: a list of primes (integers) less than or equal to n 13
Sieve of Eratosthenes But recall that the problem we really want to solve is finding a certain number of primes Because primes are not predictably spaced, we don t know how many primes we will get when finding all primes from 2 up to n We could pick some value for n and hope that it is big enough; or We could use an infinite list 14
Infinite Data Structures Lazy evaluation permits infinitely large data structures As long as they are never fully evaluated 15
Summary Evaluation in Haskell is performed differently than languages like C++, Java and Python Evaluation is lazy rather than eager Expressions are not evaluated until they are needed Unused expressions are never evaluated Lazy evaluation permits the creation of (but not complete evaluation of) infinite data structures 16
Infinite Data Structures Another example: Pseudo random numbers 17