cs109: Programming Paradigms – Haskell FS 2015

¨ 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