Presentation is loading. Please wait.

Presentation is loading. Please wait.

Com2010 - Functional Programming Lazy Evaluation Marian Gheorghe Lecture 13 Module homepage Mole & ©University of Sheffieldcom2010.

Similar presentations


Presentation on theme: "Com2010 - Functional Programming Lazy Evaluation Marian Gheorghe Lecture 13 Module homepage Mole & ©University of Sheffieldcom2010."— Presentation transcript:

1 Com2010 - Functional Programming Lazy Evaluation Marian Gheorghe Lecture 13 Module homepage Mole & http://www.dcs.shef.ac.uk/~marian ©University of Sheffieldcom2010

2 20 Lazy Programming Contents 20.1 Lazy Evaluation 20.2 Constructing Infinite Lists 20.3 List Comprehensions 20.4 Examples of List Comprehensions 20.5 Application: Regular Expressions Summary ©University of Sheffieldcom2010

3 Haskell is a lazy functional programming language: arguments of a function are only evaluated when they are needed to calculate the result of the function. This is in contrast to eager functional languages The laziness of Haskell affects the programming style. It permits extensive use of infinite data structures. Introduction ©University of Sheffieldcom2010

4 Let us use =l  for lazy and =e  for eager evaluation. Consider a switch function generalising the conditional if statement: n e_1 e_2 Lazy evaluation ©University of Sheffieldcom2010 n>0 switch n e_1 e_2 switch :: Int -> a -> a -> a switch n x y | n > 0 = x | otherwise = y

5 Lazy evaluation exploits the fact that the result of switch n e_1 e_2 only depends on one of the two arguments e_1, e_2 : switch (5-2) (2+7) (7^11) =l  switch 3 (2+7) (7^11); 3 > 0, so 1st argument is chosen =l  2+7 = 9 Eager evaluation always evaluates the arguments regardless whether they are needed. switch (5-2) (2+7) (7^11) evaluate all arguments: =e   switch 3 9 1977326743 Then evaluate switch function =e    9; Thus =e does more work than necessary! Lazy evaluation (cont) ©University of Sheffieldcom2010

6 Similarly, in lazy evaluation we do not always fully evaluate all parts of the data structure of an argument. We only evaluate those parts that are needed. head[1^1,2^2,3^3,4^4,5^5] -- identify 1st element of the list =l ⇒ head (1^1:[2^2,3^3,4^4,5^5]) -- extract it; =l ⇒ 1^1; -- evaluate; =l ⇒ 1 In eager languages we compute the argument fully before applying the function: head[1^1,2^2,3^3,4^4,5^5] -- fully evaluate the argument =e ⇒ head (1:4:9:16:25:[]); -- apply head; =e ⇒ 1 Lazy evaluation and data structures ©University of Sheffieldcom2010

7 loop :: Int -> Int loop n = loop (n+1) Evaluating loop n, eagerly or lazily, sets off an infinite computation: loop 0 =l ⇒ loop (0+1) =e ⇒ loop 1 =l ⇒ loop (0+1+1) =e ⇒ loop 2 =l ⇒ loop (0+1+1+1)… =e ⇒ loop 3 … In lazy systems loop may exist as a sub-expression head [3-1, loop 0] head[3-1,loop 0] =l ⇒ head ((3-1):[loop 0]) =e ⇒ head (2:(loop 1):[]) =l ⇒ 3-1 =e ⇒ head (2:(loop 2):[]) =l ⇒ 2 =e ⇒ head (2:(loop 3):[]) -- terminates -- loops Infinite computation ©University of Sheffieldcom2010

8 The infinite list of square numbers [n 2, (n+1 )2, (n+2 )2, (n+3 )2,…] starting at a given index n, can be defined thus: squares:: Int -> [Int] squares n = n^2: squares (n+1) squares 1 ⇒ [1,4,9,16,25,36,49,64,81,100,121,144,169,19,6,225,256, 289,324,361,400,441,484,529,576,625,676,729,784,841,… … 2147210244,2147302921,2147395600, -2147479015, -2147386332,-2147293647,-2147200960,… --??! Infinite lists ©University of Sheffieldcom2010

9 myTake :: Int -> [a] -> [a] -- take the first n elements myTake 0 _ = [] myTake n (x:xs) = x:myTake (n-1) xs myTake _ [] = [] Note 1. If n < 0 then myTake n xs returns xs. Note 2. In Prelude you may find a slightly different version of this. myTake 5 (squares 1) =l ⇒ [1,4,9,16,25] myTake 9 (squares 6) =l ⇒ [36,49,64,81,100,121,144,169,196] … a finite results involving an inner infinite computation!! A finite part of an infinite computation ©University of Sheffieldcom2010

10 Haskell has convenient syntactic abbreviations for arithmetic series: [1..] ⇒ [1,2,3,4,5,6,7,8,9,10 … [1,3..] ⇒ [1,3,5,7,9,11,13,15,17,19… [80,60..] ⇒ [80,60,40,20,0,-20,-40,-60,-80,-100… [1..7] – upper bounds ⇒ [1,2,3,4,5,6,7] [1,3..16] ⇒ [1,3,5,7,9,11,13,15] [80,60..1] ⇒ [80,60,40,20] Note. Both upper and lower bounds may be arbitrary expressions. Constructing infinite lists ©University of Sheffieldcom2010

11 iterate :: (a -> a) -> a -> [a] iterate f x = x : iterate f (f x) iterate ((+)1) 3 ⇒ [3,4,5,6,7,8,9… iterate ((*)2) 1 ⇒ [1,2,4,8,16,32,64… iterate (\x->x `div` 10) 56789 ⇒ [56789,5678,567,56,5,0,0… iterate (\x->1) 0 ⇒ [0,1,1,1,1,1,1… iterate is a higher-order built-in Haskell function mkGraph :: (Int -> a) -> [(Int,a)] -- constructs function table using map mkGraph f = map(\n->(n, f n))[0..] mkGraph id – identity function (\x->x) ⇒ [(0,0),(1,1),(2,2),(3,3)… Built-in functions for infinite lists ©University of Sheffieldcom2010

12 Pythagorean triples pythagTriples :: [(Int,Int,Int)] pythagTriples = [(x,y,z) | z<-[2..], y<-[2..z-1], x<-[2..y-1], x*x+y*y==z*z] -- x, y, z take values like in embedded loop stmts pythagTriples = [(3,4,5),(6,8,10),(5,12,13),(9,12,15), (8,15,17),(12,16,20),(15,20,25)… Please note [2..] - infinite list and [2..z-1], [2..y-1] - finite lists List comprehension ©University of Sheffieldcom2010

13 The typical form of a comprehension expression is [t | x_1<-e_1, x_2<-e_2, …,x_n<-e_n,cond_1,…cond_k] pythagTriples may be rewritten as [(x,y,z) | z<-[2..], y<-[2..z-1], x<-[2..z-1], x<y, x*x+y*y==z*z] What does this produce [(x,y,z) | z<-[2..], y<-[2..], x<-[2..z-1], y<z,x<y, x*x+y*y==z*z]?? List comprehension: definition ©University of Sheffieldcom2010 NOTHING !! (infinite loop)

14 The concept applies equally to finite lists too. [(x,y)|x<-[1,2,3],y<-[1,2]] ⇒ [(1,1),(1,2),(2,1),(2,2),(3,1),(3,2)] mkGraph’ :: (Int -> a) -> [(Int,a)] -- construct graph function table via comprehension mkGraph’ f = [(x, f x) | x <- [0..]] mkGraph' id ⇒ [(0,0),(1,1),(2,2),(3,3)… Compare with the previous definition mkGraph :: (Int -> a) -> [(Int,a)] -- constructs function table using map mkGraph f = map(\n->(n, f n))[0..] List comprehension: examples ©University of Sheffieldcom2010

15 Haskell has a built-in function zipWith zipWith :: (a->b->c) -> [a]->[b]->[c] zipWith f (a:as) (b:bs) = f a b : zipWith f as bs zipWith _ _ _ = [] For two lists [a_0,a_1,a_2,…] [b_0,b_1,b_2,…] and a function f defined for corresponding values of these lists f a_i b_i, zipWith produces [f a_0 b_0, f a_1 b_1, …] Zipping streams (1) ©University of Sheffieldcom2010

16 Comprehension makes it easy to define this function: myZipWith :: (a->b->c) -> [a]->[b]->[c] -- zips together two lists using a given function myZipWith f as bs = [ f (as!!n) (bs!!n) | n<-[0..]] For instance, myZipWith (-) [2..][1..]) ⇒ [1,1,1,1… myZipWith (+) [2..][1..]) ⇒ [3,5,7,9… Zipping streams (2) ©University of Sheffieldcom2010

17 Let [a_0,a_1,a_2,a_3,a_4,a_5,…], and we want to get: [(a_0,a_1),(a_2,a_3),(a_4,a_5),…] pairUp :: [a] -> [(a,a)] pairUp as = [(as!!n,as!!(n+1))| n<-[0,2..]] pairUp [1..] ⇒ [(1,2),(3,4),(5,6),(7,8)… Restructuring Streams ©University of Sheffieldcom2010

18 How to specify words over a given alphabet by using regular expressions. Haskell definition of a regular expression specification. Show how to match a word against a regular expression specification. There are five sorts of REs,  - this is the Greek character epsilon, which matches the empty word x - is any symbol; this matches the symbol itself (r1|r2) - r1 and r2 are regular expressions; meaning‘or’ (r1r2) - r1 and r2 are regular expressions; meaning ‘++’, i.e. r1 then r2 (r)* - r is a regular expression; meaning repetition, i.e. r taken 0 or many times Application: Regular Expressions ©University of Sheffieldcom2010

19 Examples of REs include 1. (‘a’|(‘b’’a’)) 2. ((‘b’‘a’)|(ε|(‘a’)*), These REs may be read as the set of: 1. sequences containing one ‘ a ’, one ‘ b ’ followed by one ‘ a ’ 2. sequences with one ‘ b ’ followed by one ‘ a ’ or empty word or sequences containing zero or many occurrences of ‘ a ’. Examples of REs ©University of Sheffieldcom2010

20 data RegExp = Epsilon | Literal Char | Or RegExp RegExp | Then RegExp RegExp | Star RegExp Please note that RegExp is a recursive polymorphic type. a :: RegExp b :: RegExp c :: RegExp a = Literal 'a' b = Literal 'b' c = Literal ‘c’ Then the RE re1 denoting (‘a’|(‘b’’c’)) may be represented using the above definition thus re1 = Or a (Then b c) Haskell Definition of a RE ©University of Sheffieldcom2010


Download ppt "Com2010 - Functional Programming Lazy Evaluation Marian Gheorghe Lecture 13 Module homepage Mole & ©University of Sheffieldcom2010."

Similar presentations


Ads by Google