Declarative Programming Workshop exercises set 6. QUESTION 1 Write a function fibs :: Int -> [Integer] which returns a list containing the first n numbers in the Fibonacci sequence: [0,1,1,2,3,5,8,...], where the third and subsequent numbers are the sum of the two preceeding numbers (0+1=1, 1+1=2, 1+2=3, 2+3=5, etc). We use Integer rather than Int because the numbers grow exponentially and therefore overflow native Ints quite quickly. Is the algorithmic complexity of your solution acceptable? QUESTION 2 If we do pairwise addition of the elements of the Fibonacci sequence and its tail, we get the tail of the tail of the sequence: 0 1 1 2 3 5 8 ... fibs + 1 1 2 3 5 8 ... tail fibs = 1 2 3 5 8 ... tail (tail fibs) Use this property to write a definition of allfibs :: [Integer] which is the (infinite) Fibonacci sequence (Hint: the zipWith Prelude function is useful). Define fibs in terms of allfibs. How efficient is this definition of fibs compared to your previous one? QUESTION 3 Consider the bottom-up merge sort implementation from workshop 2. >mergesort xs = repeat_merge_all (merge_consec (to_single_els xs)) > >to_single_els [] = [] >to_single_els (x:xs) = [x] : to_single_els xs > >merge [] ys = ys >merge (x:xs) [] = x:xs >merge (x:xs) (y:ys) > | x <= y = x : merge xs (y:ys) > | x > y = y : merge (x:xs) ys > >merge_consec [] = [] >merge_consec [xs] = [xs] >merge_consec (xs1:xs2:xss) = (merge xs1 xs2) : merge_consec xss > >repeat_merge_all [] = [] >repeat_merge_all [xs] = xs >repeat_merge_all xss@(_:_:_) = repeat_merge_all (merge_consec xss) With list xs of length n, what is the maximum additional space that is needed at any one time, assuming strict evaluation, for evaluating merge_consec (to_single_els xs)? What if lazy evaluation is used instead? What is the maximum additional space is needed at any one time, assuming strict evaluation, for evaluating mergesort xs? Can we do significantly better than this? QUESTION 4 Consider an interpreter for a language which produces a pair containing the result of the computation plus some debugging information, which is a string containing information about all assignment statements and function calls. Compare the efficiency of the following: a) Execution of the interpreter using strict evaluation and printing the debugging string. b) Execution of the interpreter using lazy evaluation and printing the debugging string. c) Execution of the interpreter using strict evaluation but not printing the debugging string. d) Execution of the interpreter using lazy evaluation but not printing the debugging string. e) Execution of a similar interpreter which doesn't produce the debugging string at all. QUESTION 5 Here are some students' answers to one of the questions on a sample mid-semester, which were posted on the LMS (thanks to the authors). The question asked for a Haskell function to print out Mtrees with indentation showing the structure. Compare and contrast these solutions. Can you come up with something better than all three? >data Mtree a = Mnode a [Mtree a] >print_mtree :: Show a => Mtree a -> IO() >print_mtree tree = indent_mtree 0 tree > where > indent_mtree :: Show a => Int -> Mtree a -> IO() > indent_mtree i (Mnode val children) = do > putStrLn $ (replicate i ' ') ++ (show val) > foldl (>>) (return ()) (map (indent_mtree (i+1)) children) >type Line = String > >print_mtree' :: Show a => Mtree a -> IO () >print_mtree' t = > let > toLines :: Show a => Mtree a -> [Line] > toLines (Mnode val cs) = show val : map (' ':) (concatMap (toLines) cs) > in foldl (\acc str -> acc >> (putStrLn str)) (return ()) (toLines t) > >-- A clearer version >print_mtree2 :: Show a => Mtree a -> IO () >print_mtree2 t = > let > toLines :: Show a => Mtree a -> [IO ()] > toLines (Mnode val cs) = > print val : map (putChar ' ' >>) (concatMap (toLines) cs) > in foldl1 (>>) (toLines t) QUESTION 6 Lectures have given the definitions of two higher order functions in the Haskell prelude, filter and map: filter :: (a -> Bool) -> [a] -> [a] map :: (a -> b) -> [a] -> [b] Filter returns those elements of its argument list for which the given function returns True, while map applies the given function to every element of the given list. Suppose you have a list of numbers, and you want to (a) filter out all the negative numbers, and (b) apply the sqrt function to all the remaining integers. (a) Write code to accomplish this task using filter and map. (b) Write code to accomplish this task that does only one list traversal, without any higher order functions. (c) Transform a to b.