Presentation is loading. Please wait.

Presentation is loading. Please wait.

Recursion on Lists Lecture 5, Programmeringsteknik del A.

Similar presentations


Presentation on theme: "Recursion on Lists Lecture 5, Programmeringsteknik del A."— Presentation transcript:

1 Recursion on Lists Lecture 5, Programmeringsteknik del A.

2 Lifting the Bonnet Today, we take a look `under the bonnet´ and see how functions like sum, ++, and take are defined. Recursion is the key: it lets us repeat computations, to process all the list elements in turn. Even though Haskell provides a rich set of predefined functions, you will often need to define your own.

3 Pattern Matching on Lists The most basic way to take a list apart is via pattern matching. sum [] = 0 sum [x] = x sum [x, y] = x+y … But how can we look inside a list of arbitrary length?

4 A Failed Idea What about sum (xs ++ ys) = sum xs + sum ys ??? Example: [1, 2, 3] == [1] ++ [2, 3] == [1, 2] ++ [3] Can match in more than one way!

5 A Unique Way to Represent Lists Any (nonempty) list xs can be written as xs == [y] ++ ys in exactly one way! Examples: [1] == [1] ++ [] [1, 2] == [1] ++ ([2] ++ []) [1, 2, 3] == [1] ++ ([2] ++ ([3] ++ []))

6 A Unique Way to Represent Lists Any (nonempty) list xs can be written as xs == [y] ++ ys in exactly one way! Examples: [1] == [1] ++ [] [1, 2] == [1] ++ ([2] ++ []) [1, 2, 3] == [1] ++ ([2] ++ ([3] ++ [])) New notation: y : ys 1 : [] 1 : (2 : []) 1:(2:(3:[])) Pronounced ”cons”

7 Cons Patterns Cons (:) is allowed in patterns! x : xs Matches first element. Matches remaining elements. Examples: head :: [a] -> a head (x : xs) = x tail :: [a] -> [a] tail (x : xs) = xs

8 Primitive Recursion on Lists Every list matches [] or (x : xs) Base case. Recursive case. A smaller list.

9 Summing a List sum [1, 2] =sum (1 : (2 : [])) 1 + sum (2 : []) 1 + (2 + sum []) 1 + (2 + 0)3 sum :: [Int] -> Int sum [] = 0 sum (x : xs) = x + sum xs xy z... xs

10 Quiz Give a primitive recursive definition of the function length :: [a] -> Int

11 Quiz Give a primitive recursive definition of the function length :: [a] -> Int length [] = 0 length (x : xs) = length xs + 1

12 Checking for an Element elem x xsreturns True if x is an element of xs. elem 3 (1 : [2, 3, 4…])returns True if 3 == 1, or if 3 is an element of [2, 3, 4…] elem x (y : ys) = x==y || elem x ys elem x [] = False Compare: elem x xs = or [x==y | y <- xs] yz w... ys

13 Defining ++ How can we reduce xs++ys to a simpler problem? Compute [1,2,3] ++ (4:[5,6]) from [1,2,3] ++ [5, 6]??? Reduce ys to a smaller argument? Reduce xs to a smaller argument? Compute (1:[2,3]) ++ [4,5,6] from [2,3] ++ [4,5,6]??! = [2,3,4,5,6]

14 Defining ++ (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x : xs) ++ ys = x : (xs ++ ys) How many function calls are required to evaluate [1..10]++[11..100]? [1,2]++[3] 1 : ([2] ++ [3]) 1 : (2 : ([] ++ [3])) 1 : (2 : [3]) [1,2,3]

15 Quiz Define reverse :: [a] -> [a] xwzy xyzw

16 Quiz Define reverse :: [a] -> [a] xwzy xyzw reverse (x : xs) = reverse xs ++ [x] reverse [] = []

17 Sorting now is the winter of our discontent is now of our the winter It used to be said that computers spent more time sorting than anything else!

18 Insertion Sort sort (x : xs) = insert x (sort xs) sort [] = []

19 Insertion yz...uw x insert x (y : ys) | x<=y = x : y : ys | x>y = y : insert x ys insert x [] = [x]

20 Sorting Example sort [3,1,2] insert 3 (sort [1,2]) insert 3 (insert 1 (sort [2])) insert 3 (insert 1 (insert 2 (sort []))) insert 3 (insert 1 (insert 2 []))) insert 3 (insert 1 [2]) insert 3 [1, 2] 1 : insert 3 [2] 1 : 2 : insert 3 [] 1 : 2 : [3] [1, 2, 3]

21 The Type of Sort What is the type of sort? sort :: [a] -> [a] Can sort many different types of data. But not all! Consider a list of functions, for example...

22 The Correct Type of Sort sort :: Ord a => [a] -> [a] If a has an ordering... …then sort has this type. Sort has this type because ( a -> a -> Bool Overloaded, rather than polymorphic.

23 The Correct Type of elem elem x (y : ys) = x==y || elem x ys elem x [] = False The type of elem is elem :: Eq a => a -> [a] -> Bool because (==) :: Eq a => a -> a -> Bool

24 Polymorphism vs Overloading A polymorphic function works in the same way for every type. (e.g. length, ++). An overloaded function works in different ways for different types. (e.g. ==, <=).

25 Variations on List Recursion Key idea:The recursive case expresses the result in terms of the same function on a shorter list. The base case handles the shortest possible list. But the possible variations are many.

26 Defining init Recallinit [1, 2, 3] [1, 2] x wzyx [] zyx init :: [a] -> [a] init [x] = [] init (x : xs) = x : init xs xs Different base case. Compare this to tail (x : xs) = xs

27 Checking that a List is Ordered x...uzy x <= y ordered ordered :: Ord a => [a] -> Bool ordered (x : y : xs) = x <= y && ordered (y : xs) ordered [x] = True ordered [] = True More general pattern

28 Zip: Recursion on Two Arguments x...zy a cb zip (x : xs) (y : ys) = (x, y) : zip xs ys zip [] ys = [] zip xs [] = [] What would happen if we just defined zip [] [] = []? Both arguments get smaller. Two cases remain.

29 Defining take x...zy n n-1 take :: Int -> [a] -> [a] take n (x : xs) | n>0 = x : take (n-1) xs take 0 (x : xs) = [] take n [] = [] Two base cases

30 A Better Way of Sorting Divide the list into two roughly equal halves. Sort each half. Merge the sorted halves together.

31 Defining MergeSort mergeSort xs = merge (mergeSort front) (mergeSort back) wheresize = length xs `div` 2 front = take size xs back = drop size xs But when are front and back smaller than xs??

32 MergeSort with Base Cases mergeSort [] = [] mergeSort [x] = [x] mergeSort xs | size > 0 = merge (mergeSort front) (mergeSort back) wheresize = length xs `div` 2 front = take size xs back = drop size xs

33 Merging x y x <= y? merge [1, 3] [2, 4]1 : merge [3] [2, 4] 1 : 2 : merge [3] [4] 1 : 2 : 3 : merge [] [4] 1 : 2 : 3 : [4] [1,2,3,4]

34 Defining Merge merge :: Ord a => [a] -> [a] -> [a] merge (x : xs) (y : ys) | x <= y= x : merge xs (y : ys) | x > y= y : merge (x : xs) ys merge [] ys= ys merge xs []= xs One list gets smaller. Two possible base cases. Requires an ordering.

35 The Cost of Sorting Insertion Sort Sorting n elements takes n*n/2 comparisons. Merge Sort Sorting n elements takes n*log2 n comparisons. Num elements Cost by insertion Cost by merging 105040 1000 500000 10000 1000000 50000000000020000000

36 Recursion + Comprehensions Used together, recursion and list comprehensions can be a powerful tool. Example: Define nub :: Eq a => [a] -> [a] which removes duplicate elements from a list. E.g. nub [1,2,1,3][1,2,3]. xyxy y... remove x... y y x nub

37 Completing nub nub (x : xs) = x : nub [y | y<- xs, y /= x] nub [] = [] xyxy y... remove x... y y x nub

38 Lessons Recursion works well on lists: we reduce the problem of computing a function to the same problem for a shorter list. Often the cases we consider are f [] = …base case f (x : xs) = … f xs …recursive case Sometimes list comprehensions offer a simpler alternative to recursion: use each one where it is appropriate!

39 Reading This lecture covers roughly the material in Chapter 7.1-7.5. The book also contains some excellent exercises, in particular at the end of section 7.5. Section 7.6 tackles a larger problem using the methods described in this lecture.


Download ppt "Recursion on Lists Lecture 5, Programmeringsteknik del A."

Similar presentations


Ads by Google