¨ BASEL UNIVERSITAT Thorsten M¨ oller Alexander Haesen Mihai Rapcea cs109: Programming Paradigms – Haskell Exercise 4 FS 2015 Delivery: 17.05.2015 23:59:59 Note: Please upload answers to the questions and source code before the deadline via courses.cs.unibas.ch. Running programs have to be demonstrated during the exercise slot on 18.05.2015 and 22.05.2015. Keep in mind that for the various exercises given throughout the course, you must score at least 2/3 of the total points (i.e., the sum of the points over all exercises) to be admitted to the final exam. Modalities of work: The exercise can be done in groups of at most 2 persons. Do not forget to provide the full name of all group members together with the submitted solution. Haskell note: Solutions to all assignments that implement functions should be in one *.hs file. Question 1: Find and Explain the Errors (6 points) The following lines of code are not valid Haskell. Explain for each line what is wrong and provide a correct solution where possible. a) if True then putStrLn true else putStrLn false (1 points) b) [2,3,4] : 5 (1 points) c) fooOrBar :: Int -> Int -> String fooOrBar x y | x > 0 = "foo" | y > 0 = "bar" (1 points) d) myLength :: Num a => [a] -> a myLength [] = 0 myLength x:xs = 1 + myLength xs 1 (1 points) e) size (zip [1,2] [4,6]) (1 points) f) foldl \x -> \y -> x ++ y "" ["1","2",3] (1 points) Question 2: Lazy Evaluation (Call-By-Need) (3 points) a) Consider the following two functions to calculate the Fibonacci sequence: fibs1 0 = 0 fibs1 1 = 1 fibs1 n = fibs1 (n - 2 ) + fibs1 (n - 1) fibs2 n = take n sequence where sequence = 1:1:zipWith (+) sequence (tail sequence) The first function is the widely know recursive function to calculate the Fibonacci sequence, which becomes significantly slower when n becomes bigger (the time used to calculate the kth Fibonacci number is the time taken to calculate the k-1th number plus the time taken to calculate the k-2th number). The second function uses lazy evaluation, it takes hardly any time to calculate Fibonacci numbers. Explain the algorithm used and explain the lazy evaluation used here. Question 3: Pattern Matching (4 points) Write a function that takes a lower-case string and changes all vocals to upper case in the string before returning it. Provide two versions: one using pattern matching and one using guards. Hint: you might want to use the toUpper function1 , which will be available only after importing the corresponding module; that is, add import Data.Char to the beginning of your program. Question 4: Lambda Expressions (4 points) Write a function using lambda expressions that takes a list of 2-tuples and returns a list in which each tuple’s second element is greater than first. If the two elements of a tuple are equal then the tuple should be removed entirely. If the first is greater than the second then they should switch places. Question 5: Binary Tree Traversal (4 points) In this exercise, you will implement a tree traversal function, using the given algebraic datatype Tree: 1 http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-Char.html#v: toUpper 2 1 module Main where 2 3 4 data Tree a = Empty | Node a ( Tree a ) ( Tree a ) 5 6 7 8 9 10 t e s t T r e e : : Num a => Tree a t e s t T r e e = Node 5 ( Node 3 ( Node 2 Empty Empty ) ( Node 4 Empty Empty ) ) ( Node 7 ( Node 6 Empty Empty ) ( Node 8 Empty Empty ) ) 11 12 13 main : : IO ( ) main = print ( t e s t T r e e ) a) Make the datatype Tree printable, which can be tested as shown in Line 13 of the program above using the print function, which is a monadic function (i.e., an action). This means that the Tree datatype needs be an instance of the Show type class. It does not matter how the printed Tree looks like. Finally, print testTree twice, but make sure that testTree is evaluated only once. (2 points) b) Implement a polymorphic traverse function that takes a Tree of any type of node values as its input and returns a list with all inorder traversed elements. (2 points) Question 6: Caesars Cypher (14 points) In this exercise, you will implement a real world application in Haskell. You will implement a program that encodes a string with the caesar cypher (http://en.wikipedia.org/ wiki/Caesar_cipher), and cracks the code by frequency analysis. The task is divided into multiple little steps, so don’t worry, you’ll get there eventually! The following two functions convert a letter to an integer and vice versa (a → 0, b → 1, ..., z → 25): let2int :: Char -> Int let2int c = ord c - ord ’a’ int2let :: Int -> Char int2let n = chr (ord ’a’ + n) You’ll need to import the module Data.Char to use these two functions (the function ord :: Char -> Int is needed to get a numeric representation of characters). a) Write a function shift, taking an integer and a character as input, that shifts the character n times, where n is the input integer (e.g. shift 1 ’a’ yields ’b’, shift 5 ’e’ yields ’j’): 3 shift :: Int -> Char -> Char Consider only lower case characters. If the input character is upper case, return the characters as is. (1 points) b) Write a function encode, taking an integer and a string (an array of characters) as input, returning the input string with every lower case character shifted n times: encode :: Int -> [Char] -> [Char] (1 points) You can now encode string with the caesar cyper! Let’s move on to the more fun (and complicated) part, the cracking. In the following 7 subtasks, you will implement functions that will be needed to crack the code. c) Write a function lowers, taking as input a string, and returning the number of lower case letters in this string: lowers :: String -> Int (1 points) d) Write a function count, taking as input a character and a string, and returning the number of occurrences of the character in the string: count :: Char -> String -> Int (1 points) e) Write a function percent, taking as input two integers n and m, and returning the percentage m of n: percent :: Int -> Int -> Float (1 points) f) Write a function freqs, taking as input a string, and returning an array containing the frequencies of the characters a through z in the given string (don’t count the upper case letters): freqs :: String -> [Float] Hint: the array a through z can be obtained by the expression [a..z]. Use the functions lowers, count and percent (1 points) g) Write a function chisqr, taking as input two float arrays os and es (assume that they both have the same length), and returns the sum of the following array or (pseudo code): for every e in es, o in os: append ((o - e) ^ 2 / e) to or 4 chisqr :: [Float] -> [Float] -> Float Hint: use the zip function to combine the two arrays into an array of tuples, and then compute the Chi-Squared difference over every tuple. (2 points) h) Write a function rotate, taking as input an integer n and a list, and returning the list rotated n times (e.g. rotate 2 [1,2,3,4,5] becomes [3,4,5,1,2]) rotate :: Int -> [a] -> [a] (1 points) i) Write a function positions, taking as input a variable and a list of variables of the same type, and returning an array of positions of the first parameter in the second one. To be able to implement this function, an equality operator must be defined over the variable-type. Make sure that this assumption is imposed. positions :: a -> [a] -> [Int] Examples: positions 3 [1,3,3,7] yields [1,2] positions ’t’ "test" yields [0,3] (1 points) We now have all required functions available to decipher the encrypted message! We just need the following frequency table, representing the average frequency of every character in english text: table :: [Float] table = [8.2, 1.5, 6.1, 7.0, 7.5, 1.9, 1.0, 2.4, 2.8, 0.2, 0.1, 0.2, 4.3, 12.7, 2.2, 2.0, 0.8, 4.0, 2.4, 6.7, 6.0, 6.3, 9.1, 2.8, 2.0, 0.1] j) Implement a function crack, taking as input the encoded string and returning the decoded message: crack :: String -> String The steps to decipher the message are the following (the Haskell function does not need to be implemented sequentially): • compute the frequencies of every character in the input string using the function freqs, save them in an array table’ • compute an array of Chi-Squared differences between the frequency-table and table’ shifted 0 to 25 times (resulting in an array of 25 Chi-Squared differences), using the functions chisqr and rotate. Save these differences in an array chitab. 5 • Compute the index of the smallest Chi-Squared difference in the array computed in the last step (e.g. the index of the minimum of chitab) using the function positions • This index will give you the shift used for the encoding of the message. Decode the message by shifting the code the other way around. If you have successfully implemented the crack-function, try to decipher the following message: k dkvo dyvn li kx snsyd, pevv yp cyexn kxn pebi, csqxspisxq xydrsxq. (4 points) Question 7: Algebraic Data Types & Type Classes2 (0 points) Define an algebraic data type Vehicle that can be either a Car, Truck, Motorcycle, or Bicycle. A Vehicle has the manufacturer (String) and the horsepower (Float) as components. Define Bicycle to have a fixed horsepower of 0.5. Write a function that returns the cumulated horsepower of a given list of vehicles. Write another function that, given a list of vehicles, returns the vehicle with the most horsepower. You should implement comparison of vehicles using type classes. Question 8: Circle Test2 (0 points) Write a function inCircle r x y k s that returns the coordinates of all the points from the discrete interval ([-k..k],[-k..k]) with step size s that are inside of the circle with the radius r and the center (x,y). 2 Optional task. There is no need to solve this task nor is it graded if done. 6
© Copyright 2024