This action might not be possible to undo. Are you sure you want to continue?
Functional Programming in Haskell Unassessed Exercises Tony Field (Room 376)
Answer as many as you can at your own speed. These exercises do NOT need to be submitted for marking. Model answers to each set will be handed out in due course.
Functional Programming in Haskell
Unassessed Exercise Sheet 1: Expressions, Built-in Types and Prelude Introduction
All the problems on this sheet can be solved by typing expressions at the Hugs prompt: Prelude> type your expression here and press RETURN First read the laboratory notes provided, which introduces you to the Department’s computers and tells you how to start the Haskell (Hugs) system. As you do each question, read any accompanying notes carefully. Also, most importantly, make up your own problems and try them out too. Note that an important objective of this first batch of problems is to introduce you to some of Haskell's built-in types and functions (Haskell's so-called prelude). Within Hugs, you can find out more about any known identifier, including types, functions, classes etc., by typing :info identifier (or use :i for short), e.g. :info +, :i Int etc. To get a list of all function names that are in scope at any time type :names (or :n). You will probably not be able to complete these exercises in the two lab sessions allocated before Thursday's lectures, but don't panic! There are more questions here than you need, so skip over some of them once you've got the hang of it. 1. Type in and evaluate the following Haskell expressions: (a) (b) 2 - 3 - 4 2 - ( 3 - 4 )
Q: What does this tell you about the associativity of the Haskell ‘-’ operator? Remark: a left-associative operator op implies that successive applications are bracketed from the left, so that a op b op c is evaluated as ( ( a op b ) op c ). For a rightassociative operator, it would be evaluated as ( a op ( b op c ) ). (c) (d) (e) 100 `div` 4 `div` 5 100 `div` ( 4 `div` 5 ) 2 ^ 3 ^ 2
Remark: div is the prefix integer division function (quotient function), as in div 12 5 which gives the answer 2. The back quotes in `div` turns the prefix function into an infix operator, as in 12 `div` 5 (try this out). Do not confuse back quotes with the forward quotes that are used to delimit characters. Note also that an infix operator can be turned into a prefix function by enclosing it in brackets. For example, the expression (+) 8 5 means the same as 8 + 5. Q: What can you say about the associativity of the `div` and ^ operators? (f) (g) 3 - 5 * 4 2 ^ 2 * 3
<=.(h) 2 * 2 ^ 3 Q: What can you say about the relative precedence of the -. The associated arithmetic is therefore subject to artefacts such as rounding error (we don’t get exactly the right answer) and overflow (the answer is too big or too small for the computer to represent). (i) (j) (k) (l) (m) (n) ( 3 ord chr chr 'p' 'h' + 4 .567 = 19. Integer. Finally. If op1 has a higher precedence (a higher number) than op2 then a op1 b op2 c evaluates to ( ( a op1 b ) op2 c ) and a op2 b op1 c evaluates to ( a op2 ( b op1 c ) ). The operators ==. ‘A’. * and ^ operators? Remark: An operator’s precedence is an integer in the range 0-9 which is used to determine the order in which multiple operator applications are evaluated.g. Q: What does this tell you about the relative precedence of prefix function application and infix function application? Remark: (o) gives a rather unintuitive answer. Note: the function truncate is a prefix function which rounds a floating point number (Float) down to the nearest Int. See the attached ASCII table and check it out by applying the ord function to various characters. as with * and + respectively. /=. >= also work on characters using the ASCII numeric representation in the obvious way. We say that op1 binds more tightly than op2. for practice. 'a' < 'b' gives True because ord 'a' < ord 'b'. are encoded by computers as numbers.ord 'A' ) ) /= 'p' <= 'u' Remark: The alphabetic characters ‘a’. (o) (p) sqrt 2 ^ 2 sqrt 2 ^ 2 – 2 (try also sqrt 2 ^ 2 == 2 ) (sqrt is a function that calculates the square root of a given number). The inverse of ord is called chr. >. Experiment with the other Haskell operators. The reason is that floating-point arithmetic (arithmetic with Floats and Doubles) is only an approximation to arithmetic with real numbers.8 ^ 2 .1 ) ( ord 'X' + ( ord 'a' . e. (q) not True || 5 < 6 `mod` truncate 2. <. work out how Haskell brackets the following expressions and test your answers by typing in equivalent expressions with the brackets explicitly inserted. for example.5 + 4 ) 'a' ( ord 'b' . The most commonly used numbering scheme is called the ASCII encoding.g. mathematically. To understand this fully you will have to wait until the lectures on computer arithmetic which will follow shortly in another course.5 ) == ( 3 . e. ‘B’ etc. truncate 19. an infinite and continuous object (the infinite real line). The ASCII code for a character c can be obtained by the Haskell function ord (for ordinal value). Computers use a finite representation for what is. ‘b’. Float and Double types). max and min respectively return the larger and smaller of two given numbers (works with Int.
For each.9 2 ^ truncate pi ^ 2 < 0. Using the minimum number of brackets as possible.5 0. Where the type is incorrect work out how Haskell has implicitly bracketed the expressions. work out whether the expression is correctly typed or not.8 2. (a) (b) (c) (d) (e) (f) (g) (h) (i) 2 * 3 == 6 6 == 2 * 3 2 + 3 == 5 not 2 * 3 == 10 3 == 4 == True if True then False else True == if 3 == 4 then ord 'a' else ord ord if 3 == 4 then 'a' else 'b' 8 > 2 ^ if 3 == 4 then 2 else 3 True 'b' + 1 == False . where necessary. write down expressions which evaluate to True iff: (a) (b) (c) (d) (e) (f) (g) (h) (i) (j) 100 is exactly divisible by 10 22 is even (do not use the built-in even function in Haskell!) The penultimate digit of 139 is different from the last digit of 55 The letter ‘z’ is the 26th lower-case letter of the alphabet The letter ‘c’ is either the 1st or the 3rd lower-case letter of the alphabet The letter ‘b’ is neither the 1st nor the 3rd lower-case letter of the alphabet As for (e) but this time do not use the not operation 100 lies between 50 and 200 10 does not lie between 50 and 200 As for (h) but do not use the not operation 4.8 && pi > sin 0.e.(r) (s) (t) 1 + sin 0. write expressions to find the values of the following: (a) (b) (c) (d) (e) The last digit of the number 123 The penultimate digit of the number 456 The eighth lower-case letter of the alphabet (i. All of the following expressions are syntactically correct. but some of them have some form of type error. mod and ord. Using the Haskell operators div. starting from ‘a’) The “middle” upper-case letter of the alphabet The minimum number of 13 gallon barrels needed to hold the entire contents of an 823 gallon vat 3.sqrt 5 * 4 cos 2 * pi ^ 2 >= 4 || sin pi < max 0.7 ^ 2 .
Solve this problem using both approaches. suggest where they might be added to make the types of the expressions correct.e. which gives x the value 5 in expression blah we can also write let (x. 10 ). 'c' ) ) Remark: The ordering used by Haskell's comparison operators is lexicographical . Write a let expression which converts the polar coordinate (1. >. What types do these expressions now have? 5. Write a qualified expression using a let which finds the two roots of the equation ax2 + bx + c = 0 using the formula −b ± √(b2 − 4ac) _______________ 2a in the case where a=1. there are built-in functions called fst and snd which will respectively deliver the first and second elements of a given pair. 4 ) ) ( 'a'. Remark: Haskell allows you to match tuples in qualified expressions. 2 ) < ( 1. i. using tuple matching and by explicit use of the so-called projectors fst and snd.y) = (5. Using `div` and `mod` write an expression which converts time (an integer called s. Alternatively. To see how. into a triple of integers representing the number of (whole) hours. ( 3. ( 9. <=.6) in blah which simultaneously gives x the value 5 and y the value 6.5. . 9 ).it works from left to right just like the ordering used in a dictionary. minutes and seconds since midnight. say). 5. Write your expression in such a way that the term inside the square-root is computed only once and return the two roots in the form of a tuple. <. >= also work on tuples. 4 ) ( ( 4. /=. in the same way that we can write let x = 5 in blah. 7. The operators ==. 1 ) ) > ( ( 1. 5.Assuming that the expressions with incorrect types were a result of the programmer accidentally omitting parentheses. b=4 and c=1. ( 3. ( 3. As an example. and the y-axis displacement is r sin θ. try the following: (a) (b) (c) ( 1. Use a let to perform the calculation for the specific case where s=8473. 'b' ) ) < ( 'a'. 6. representing the number of seconds since midnight. 8. A polar coordinate (r.π/4) into cartesian coordinates represented by a pair of Floats.θ) can be converted into cartesian coordinates using the fact that the x-axis displacement is r cos θ.
Note that a triangle whose sides are of length a. The Fibonacci numbers are defined by the recurrence F0 = 0 . if not the later lectures will help to explain. 1. Write a function adddigit of two arguments which will add a single-digit integer onto the right-hand end of an arbitrary-size integer. 2. do not use the predefined functions div and mod). Write a function fact which defines the factorial of a non.Functional Programming in Haskell Unassessed Exercise Sheet 2: Functions For each function you define enter its type as well. The factorial of a non-negative integer n is denoted as n! and defined as: n × (n-1) × (n-2) × (n-3) … × 2 × 1 0! is defined to be 1. Given the type synonym type Vertex = ( Float. Float ) write a function distance :: Vertex -> Vertex -> Float that will calculate the distance between two points. You may be able to work out what its doing in these cases. For example.e. Ignore the possibility of integer overflow. Write a function convert which converts a temperature given in oC to the equivalent temperature in oF. when you are happy that your function is correct. 5. b and c has an area given by s ( s −a)( s −b)( s −c) where s = (a + b + c) / 2 . Write a function quotient which defines integer division using repeated subtraction. The Hugs system will automatically work out (infer) the type of your function and you can check this by typing :type <fun> where <fun> is the name of your function. As an additional exercise. delete the type definition using the editor and then return to the Hugs system. 7. Implement the division by repeated subtraction (i. Write a function remainder which defines the remainder after integer division. each represented as a Vertex. In some cases you may be surprised to see that the inferred type is different to the type you originally wrote down. Ignore the possibility of integer overflow.negative integer. Using the distance function write a function triArea :: Vertex -> Vertex -> Vertex -> Float that will calculate the area of the triangle formed by the three given vertices. adddigit 123 4 should evaluate to 1234. Ignore division by zero.) 3. 4. 6. (To convert from oF to oC the rule is to subtract 32 from the temperature in oF and then multiply the result by 5/9. Ignore the possibility of division by zero.
Inspired Fn by your earlier fib' function build a function gRatio which takes a threshold value e of type F Float and which returns the value of n +2 where n is the smallest integer satisfying Fn +1 consecutive Fibonacci numbers converges on the golden ratio. Cut out the largest square you can and the sides of the rectangle that remains are also in the golden ratio. If you think about the way a call to this function will be evaluated you will notice that there is an enormous degree of redundancy. given an integer n=0. its binary representation is just d. to perform the division above. ii.F1 = 1 Fn = Fn-1 + Fn-2. G. Curiously. n>1 Write a Haskell function fib that. the call Fn-3 three times. If d is less than 2. you must first cast the integers representing the various Fibonacci numbers into floating-point numbers using the built-in function fromInt. which is either 0 or 1. (Notice any pattern?!) A much more efficient way to compute the nth Fibonacci number is to use an auxiliary function that includes as arguments the kth and the (k+1)th Fibonacci numbers for some number k..e. 9. i. The remainder gives the last (rightmost) digit of the binary representation. Hint: notice that you now need three consecutive Fibonacci numbers instead Fn Fn +1 of just two. The true picture will emerge later in the course. Otherwise. The golden ratio has this property: take a rectangle whose sides (long:short) are in the golden ratio. divide by 2. is a beautiful number discovered by the ancient Greeks and used to proportion the Parthenon and other ancient buildings. returns the nth Fibonacci number by encoding the above recurrence directly.can you see how to generate them from the kth and the (k+1)th? You will also need to carry a third argument which keeps count of how many (more) numbers in the sequence to calculate before arriving at the required (nth) one. 2. he function call Fn-2 is made twice. From this you should be able to work out that G = 1+ 5 (do it!). The idea is that the recursive call is made with the (k+1)th and the (k+2)th Fibonacci numbers . that is) can be obtained using Haskell's abs function. Fn-4 five times etc. For a given n. iii. The absolute value of a number (ignoring its sign. A decimal number d may be converted to binary representation by the following algorithm: i. 1. The preceding digits of the binary representation are given by the binary representation of the quotient from step (b). . Also. The Golden Ratio. lim n→∞ Fn +1 F − n +2 < e . 8.. . For now you can think of this function as having type fromInt :: Int -> Float. it can also be shown that the ratio of 2 Fn +1 → G . Define an auxiliary function fib' which works in this way and redefine the original fib function to call fib' appropriately.
Write a function add2 which will add two non-negative numbers using succ and pred only. Do not use div or mod. Write a function newbase of two arguments which converts a number to its representation in some specified base ≤ 10. each of which can be either zero or non-zero. Use multiple recursion equations and do not use conditional expressions. succ 4 returns 5. Estimate the cost of your concatenate function in terms of the number of multiplications and subtractions it performs. Remember that there are two parameters. 11. Given an integer argument the built-in function succ returns the next largest integer. This will be a decimal number consisting only of the digits 0 and 1. 14. The number of arrangements (permutations) of r objects selected from a larger set of n objects is denoted as nPr and defined as: n × (n-1) × (n-2) … × (n-r+1) n! or ______ (n-r)! Define a recursive function perm such that perm n r evaluates nPr (do not use fact) 12. Can the number of equations be reduced? 13. Write a function concatenate which concatenates the digits of two non-zero integers. 10. so a strict definition will require four equations. Use the earlier function chop. Use multiple recursion equations with pattern matching rather than conditionals. concatenate 123 456 evaluates to 123456 but concatenate 123 0 evaluates to 123. The function pred does the opposite. As an example. Write a recursive function larger to determine the larger of two positive integers. Write a recursive function chop which takes an n-digit number and which uses repeated subtraction to define a pair of numbers which are the first n-1 digits and the last digit respectively. . For example.Write a function binary which takes an integer and converts it to its binary representation. This is a generalisation of the binary function above. binary 19 should produce the answer 10011. Hint: write down the left-hand sides of the four recursion equations first. The value produced will be a decimal number containing only the digits which are valid in the new base. 15.
||. max and min respectively: sum :: [ Int ] -> Int Returns the sum of the elements of the list (also works with Integer. length :: [ a ] -> Int The number of items in a list (!!) :: [ a ] -> Int -> a xs !! n returns the nth element of xs starting at index 0 null :: [ a ] -> Bool True if the list is empty. ++. b ) ] -> ( [ a ]. By the end of the course you should be familiar with all of them and be able to use them to best advantage. Float and Double types) . *. Float and Double types) product :: [ Int ] -> Int As above. &&. False otherwise (++) :: [ a ] -> [ a ] -> [ a ] Joins (appends) two lists head :: [ a ] -> a Returns the first item in the list (equivalent to index 0) tail :: [ a ] -> [ a ] Returns the list with the first item removed take :: Int -> [ a ] -> [ a ] take n xs returns the first n items in xs drop :: Int -> [ a ] -> [ a ] drop n xs returns the rest of xs after the first n items have been removed zip :: [ a ] -> [ b ] -> [ ( a. b ) ] Zips the elements of the two lists pairwise unzip :: [ ( a. [ b ] ) Opposite of zip The following are list generalisations of the functions +. but returns the product and :: [ Bool ] -> Bool True iff every element of the list is True or :: [ Bool ] -> Bool False iff every element of the list is False concat :: [ [ a ] ] -> [ a ] Joins the elements of a list of lists maximum :: [ Int ] -> Int Delivers the largest element in the given list (also works with Integer.Functional Programming in Haskell Unassessed Exercise Sheet 3: Lists and Higher-order Functions Many of these questions concern some of Haskell's (first-order) built-in list processing functions.
3 ) ] maximum zip "abc" [ 1. 6.2). ( 'd'. determine the resulting value. 'l'. 'o' ). If they are. "with".1). 2. 1 ] foldr maximum  [ [0. "abc" ] ) foldr1 (||) ( map even [ 9. foldl1 does the same but associates to the left.. 3 ] concat [ tail [ "is". 2 ). 'd' ).2] ] zipWith (&&) [ True ] ( filter id [ True. "ab". ( 'd'. (a) (b) (c) (d) (e) (f) (g) (h) (i) (j) (k) (l) (m) (n) (o) (p) (q) (r) (s) (t) (u) (v) (w) (x) (y) (z) "H" : [ 'a'. "11". (2. in between each element of the given list. 'l' ] ( 'o' : ['n'] ) ++ "going" "Lug" ++ ( 'w' : "orm" ) if "a" == ['a'] then  else "two" "let xs = "let xs" in length [xs] tail "emu" : drop 2 "much" head [ "gasket" ] zip "12" ( 1 : [ 2 ] ) tail ( (1. True /= False ] sum [ length "one". 'g' ) ] and [ 1 == 1. in right-associative fashion. False. length  ] [ ( True. [3. 'k'. 8 . 's'. [ 2. 1. 1 ). ( 'a'. 3.minimum :: [ Int ] -> Int As above but delivers the smallest element Now the higher-order functions: map :: ( a -> b ) -> [ a ] -> [ b ] Applies the given function to each element of the given list filter :: ( a -> Bool ) -> [ a ] -> [ a ] Returns a list of those elements in the given list which satisfy the given predicate zipWith :: ( a -> b -> c ) . 'e'.3) ] /= head [ (2. 1 ]] length  + length [] + length [[]] null ( [ "".3) ) head [ 1. "standing" ] ] map head [ 1. ( 'a'. (3. If not explain the cause of the type error.3). (2. True ) ] unzip [ ( 'b'. False ] ) . 'a' == 'a'. 4 ] minimum [ ( 'b'.[ a ] -> [ b ] -> [ c ] Applies the given binary function pairwise to the elements of the two lists foldr1 :: [ a -> a -> a ) -> [ a ] -> a Returns the result of inserting the binary operator. By inspection (check them if you like by typing them into Hugs). determine whether the following are correctly typed. "not". 3 ] !! 0. "111" ] !! 1 ) zip [ 5.n ] filter null ( map tail [ "a".1]. foldr :: [ a -> b -> b ) -> b -> [ a ] -> b As above but with a separate right unit. foldl does the same but associates to the left. 2 ] ) zipWith (:) "zip" "with" foldr max 0 [ 0. "1".
2. 1. 2. ( 3. Don't forget to add the head element back! 5. 1 ] < [ 2. The 'dictionary' style (lexicographical) ordering is perhaps more apparent here. 1 ] evaluates to 1.g. For instance. transpose "UVWXYZ" "fedcba" "ecabdf" should evaluate to "VXZYWU". 1. 2 ] [ 1. substring "sub" "insubordinate" evaluates to True whilst substring "not" "tonight" evaluates to False. "way" ) ] < [ ( 2. <=. Write a function substring :: String -> String -> Bool which returns True iff the first given string is a substring of the second.2. "ways" ) ] Now write a function precedes (equivalent to (<)) which takes two Strings and evaluates to True if the first is lexicographically less than the second. "abcde" and "eabcd" define a transposition in which the last character of a 5-character string is moved to the start. e. Using pos define quicksort :: [ Int ] -> [ Int ]. Hint: use pos and !!. 4. pos '4' [ 3. Define a function transpose to transpose the characters of a string as specified by two anagrams. 6. Try the following: (a) (b) (c) (d) (e) (f) "Equals" == "Equals" "dictionary" <= "dictator" "False" > "Falsehood" [ 1. myMaximum etc. make sure that you can write them all this way.) Note that to test them you will have to use different names to those used in the prelude. Hint: one way to do this is to first generate the list of all suffixes of the second string and then compare the first string with the appropriate prefix of each suffix. The overall result is True iff at least one of these comparisons succeeds (use the or function). Write a function pos to find the position of a specified Int in a list of Ints. (Most of these will have been covered during the lectures. Assume that the integers are indexed from 0 and that the specified character will always be found). Write a function twoSame :: [ Int ] -> Bool which delivers True iff the given list of integers contains at least one duplicate element. 4. and those greater than it. 3 ] < [ 1. Two anagrams can be considered to define a transposition of the characters of a third string.g. >. The operators ==. "ways" ). The simplest version of this famous algorithm works by using the first element of a list to partition the remainder of the list into two sublists: those smaller than (or equal to) the head element. noting that all three strings are the same length and that each character of the second string is unique and appears exactly once in the third. The two sublists are sorted recursively and the resulting lists joined together. /=. >= also work on lists. . <. e. 0. 7. 3. 5 ] [ ( 1. myLength. Provide recursive definitions for each of the built-in functions defined above.
squash f [ x1. x4 ] -> [ f x1 x2. 16. 13. 15.g. Sometimes we need to compose a single-argument function with a binary function. Estimate the cost of the new version of the function in terms of the number of ':' operations it performs to reverse a list of length n. x3. 10. 9. (Note: this function is called reverse in the Haskell prelude. However. Using explicit recursion and pattern matching and 2. 3. Write a function limit :: ( a -> a -> Bool ) -> [ a ] -> [ a ] which again checks for convergence but this time returns the list elements up to the point where the convergence function delivers True. Write a function splitUp which returns the list of words contained in a given String. Write a function converge :: ( a -> a -> Bool ) -> [ a ] -> a which searches for convergence in a given list of values. For example. Write a function squash :: ( a -> a -> b ) -> [ a ] -> [ b ] which applies a given function to adjacent elements of a list. 2. x2... (+) ) 4 7. 5. right 's' "Hope" evaluates to "Hopes". a String) returns a pair consisiting of the next word in s and the remaining characters in s after the first word has been removed. Haskell's (. This requires you to define an auxiliary function with one extra argument to represent the “accumulator” (this will be  when initally called). Use the function nextWord defined above. This is a generalisation of the takeWhile function.e. '\t'.) function will . 12. If no limit is found before the list runs out. 14. converge (==) [ 1. print an error message. as in ( succ . Words are assumed to be separated one or more whitespace characters (' '. Implement the function 1. In terms of zipWith (hint: make use of the tail function). Write a polymorphic function right which will insert a single item at the right-hand end of a list of items. Write a polymorphic function backwards which uses right to reverse a list of objects of arbitrary type. backwards "bonk" evaluates to "knob". e. Estimate the cost of your function in terms of the number of ':' operations it performs to reverse a list of length n. It should apply its given function to adjacent elements of the list until the function yields True. 17. 5. 5 ] should return 5. Use this to test your limit function 1−r with an expression of the form sum ( limit f ( map (r^) [0. Write a polymorphic function perms which generates all permutations of a given list. '\n'). Now modify your backwards function so that it uses repeated applications of ':' to an accumulating parameter instead of using right. in which case the result is the first of the two convergent values. Note: if you wish you may use the built-in function isSpace. f x3 x4 ].] ) for some convergence function f. 4.8. For example. Write a function nextWord which given a list of characters s (i. f x2 x3.) 11. Mathematics tells us that if 0 ≤ r < 1 then ∑r n =0 ∞ n = 1 . (yielding 12) for example.
Test your function by forming a pipeline from the function list [ (+ 1). You can imagine this as being like a conveyor belt system in a factory where goods are assembled in a fixed number of processing steps as they pass down a conveyor belt. pred ] with the resulting pipeline being applied to the input list [ 1. Each process performs a part of the assembly and passes the (partially completed) goods on to the next process.'. map and the identity function id. foldr. each function in the original function list is applied in turn to each element of the input (assume the functions are applied from right to left in this case).>? 18. Hint: Notice that if f :: a -> a then map f is a function of type [ a ] -> [ a ]. write a function pipeline which given a list of functions. What is the most general type of <. In such a pipeline. 3 ]. (* 2). .only compose two single-argument functions. each of type a -> a will form a pipeline function of type [ a ] -> [ a ]. Define a new composition operator <. Using '. 2.> that will allow compositions of the above form.
The department’s staff database comprises a list of staff records described by the Haskell type synonym: Database = [ Ustaff ] Associated with each employee is also a basic information record comprising the employees name. A university Computing department has two types of employee: Teaching staff and Support staff. whose dimensions are given by a single Float. Define a new data type Possibly which can represent success/failure outcomes. Augment the area function accordingly.y') is given by L = sqrt( (x-x')^2 + (y-y')^2 ) 3. Possibly and tableLookUp should behave identically! 1 . c is given by: A = sqrt( s (s-a) (s-b) (s-c) ) where s = ½(a+b+c) 2. Failure should be represented as a parameterless constructor whilst success should be a constructor that carries with it a result value of arbitrary type (Possibly should be polymorphic). Now augment Shape with (convex) polygons. whose dimensions are specified by the length of the triangle's three sides (all Floats). specified by a list of the polygon vertices ((x. Each course is identified by a unique number. Software or Theory. Teaching staff have an associated research section which is either Systems.Functional Programming in Haskell Unassessed Exercise Sheet 4: User-Defined Data Types 1. Recall: The area of a triangle with dimensions a. Write a function age which given the birth date of a person and the current date returns the person’s age in years as an Int. Note: the area of a convex polygon is the area of the triangle formed by its first three vertices plus the area of the polygon remaining when the triangle has been removed from the polygon. Define a function tableLookUp :: a -> [ ( a. squares. and a list of courses that they teach. and circles. Define a data type Shape suitable for representing arbitrary triangles. b. Define a data type Sex suitable for When you have completed the solution take a look at the type Maybe and the function lookup in the Haskell prelude. 4. b ) ] -> Possibly b which searches for an item in a table of item/value pairs.y) with vertex (x'. otherwise it should report failure. sex (male or female).1 5. If the item is found the lookup should succeed with the corresponding value. each specified by its radius (again a Float). Define a function area :: Shape -> Float which returns the area of a given Shape. Support staff are classified as either Administrative or Technical. date of birth and salary. Note: the length of the line joining vertex (x. Define a type synonym Date suitable for representing calendar dates.y) coordinates in the Cartesian plane).
You may find that your solution is different to the model answer. as described. Notice that at this stage the derived definition of '==' does not “know” that the times 1400hrs in 24-hour format and 2-00pm represent the same time! . 11. Write a function swap which will interchange the subtrees of a binary tree at every level. Use deriving to enable times to be both displayed and compared for structural equality. For a list xs how does the value of ends ( swap ( build xs ) ) relate to xs? 10. A queue is a data structure which can contain zero or more elements. (a) (b) (c) For the above Database write Haskell functions to compute the following: The total number of support staff The name of the teacher who teaches a given course The total salary bill for the department (this is ususally a very small number!) Hints: You may find it useful to define the following auxiliary functions: • • • name and salary for returning the name and salary of a given Ustaff (functions that pick out elements of a tuple like this are sometimes called projector functions) isSupport which will return True iff a given Ustaff is a support staff teaches. Design a data type Time which is capable of representing time in two ways: either in 24hour format (for example 1356hrs) or in conventional hours and minutes. Define a polymorphic data type for describing binary trees in which all values are stored at the leaves. with an additional flag to indicate whether the time is am or pm (use an additional enumerated data type AmPm to represent the two possibilities). 6. Define a data type to represent queues of customers represented by words containing their names. Write a function build which will construct a balanced binary tree from a non-empty list of numbers by using splitAt. Write functions nobody to create an empty queue. which given a course number and a Ustaff delivers True iff the given Ustaff teaches the given course 7. Write a function ends which will convert a binary tree to a list preserving a left-to-right ordering of the numbers at the fringe of the tree. 8. You could issue an error message if build is applied to an empty list. ordered by their arrival times. Remark: There is no unique way of representing the database. arrive to add a new customer to the rear of an existing queue.representing a person’s sex and define a type synonym Empdata for representing each basic information record. Finally define an algebraic data type for Ustaff. first to find the first (least recently arrived) customer in a queue and serve to remove the first customer from the front of a non-empty queue. there should be no concept of an “empty” tree. Remark: A balanced binary tree has the property that at each internal node the sizes of the left and right subtrees differs by at most 1. 9.
Test your implementation by typing various expressions of type Time on the Haskell command line.12. A time in 24-hour format should be displayed in the form “xxxxHRS” where xxxx is the four-digit 24-hour time. hours (hh) and minutes (mm) should be displayed in the form “hh:mmAM” for times before midday. Now make Time an instance of the class Show and provide a definition of the function showsPrec (see below) for displaying Times (see below). The following questions are quite hard an invite you to think about higher-order functions over trees. Tues ] showsPrec is of type Int -> T -> String -> String. False otherwise. When a new type T is made an instance of Show the showsPrec function defines how to convert an object of type T into a (printable) string. The Haskell system uses this function to print representations of objects. Remark: The class Show has a member function showsPrec. 15. Test your implementation by comparing various Times for both equality and inequality (recall that '/=' is defined by default in terms of '==' in the definition of class Eq). with the special cases that 12-00pm and 12-00am should be displayed as “Midday” and “Midnight” respectively. simply ignore it! The String argument is an accumulation of the result string that will form the final printable representation. For example given the representations of 1514hrs and 3:14pm equaltime should return True. so you will need to append your representation as the leftmost argument of '++'. Write a function to24 which. 13. given a time in conventional format will convert it to 24hour format. All you have to do here is append to this your chosen representation of T. for the Time data type you will need two rules. Do not worry about this here. Using to24 write a function equaltime which given two times in either format returns True if the times are the same. one for each time format. and “hh:mmPM” for times after midday. There is a short introduction to this in the lecture notes. At the same time define '==' on Times which delivers True if two times are equal regardless of the format of the time. Q16-18. rather than those over lists that you have seen so far. For a time in the conventional format. 14. The first argument is a precedence which can be used to print representations using the minimum number of brackets. . although we will not be covering it in the lectures themselves. Now delete the deriving statement and explicitly add Time as an instance of the class Eq. as in: Main> “Hello” “Hello” Main> [ Mon. Note that the result list is accumulated from right to left. Tues ] [ Mon.
16. (You can best picture this as a transformation on a tree which replaces the empty tree by a given base case. . b ) -> [ b ] which takes a value and a value-attribute pair and which returns the singleton list consisting of the attribute if the two values match.  otherwise. The first function should be applied to values at the leaves and the second to values at the nodes. one to transform a given leaf value and one to reduce an internal node. Use an auxiliary function pass :: a -> ( a.) Define functions in terms of fold3 to do the following: (a) (b) (c) (d) (e) Count the number of leaves in a given tree (an empty tree should not be counted as a leaf) Sum the values in a given tree of Ints Perform a left-to-right in-order flattening of a tree to yield a list Perform a right-to-left in-order flattening of a tree which delivers the same result as (c) only in the reverse order (do this by modifying your folding functions. do not use a reverse function on the result from (c)!!) Search a tree of value-attribute pairs of the form (v.a) for all occurrences of a given value. 18. Define the function fold3. Arrange it so that values at the nodes may have a different type to those at the leaves. Define a new polymorphic data type Tree3 which stores values both at the internal nodes and the leaves. and for which there is an Empty data constructor for representing empty trees. 17. and the leaf and node constructors with the supplied functions. Define a function map3 which maps two functions over a given Tree3. a version of fold for objects of type Tree3 which takes two functions. The attributes associated with each such value in the tree should be returned as a list.
Ord.b) deriving (Eq. Enum.APPENDIX: Summary of the Haskell Prelude Types and Synonyms data () = () deriving (Eq.b) = (a. Ord. Read. Bounded) Instances: Read Show data [a] =  | a : [a] deriving (Eq. Show) type FilePath = String data Float Instances: Eq Ord Enum Read Show Num Real Fractional RealFrac Floating RealFloat data Int Instances: Eq Ord Enum Read Show Num Real Integral Bounded data Integer Instances: Eq Ord Enum Read Show Num Real Integral data IO a Instances: Show Functor Monad data IOError Instances: Show Eq data Maybe a = Nothing | Just a deriving (Eq. Bounded) data Char Instances: Eq Ord Enum Read Show data Double Instances: Eq Ord Enum Read Show Num Real Fractional RealFrac Floating RealFloat data Either a b = Left a | Right b deriving (Eq. Ord. Show. Ord) Instances: Read Show Functor Monad MonadZero MonadPlus data (a. Show) Instances: Functor Monad MonadZero MonadPlus data Ordering = LT | EQ | GT deriving (Eq. Read.) EQ False GT Just Left LT Nothing Right True . Enum. Ord. Read. Show. Bounded) type ReadS a = String -> [(a. Read. Bounded) Instances: Read Show data a->b Instances: Show data Bool = False | True deriving (Eq. Enum. Ord. Ord.String)] type ShowS = String -> String type String = [Char] data Void Constructors  : (.
Ord class (RealFrac a. Eval class (Eq class class (Num a. Integral b) => a -> b -> a negative exponent allowed abs :: Num a => a -> a accumulate :: Monad m => [m a] -> m [a] acos :: Floating a => a -> a acosh :: Floating a => a -> a .) :: (b -> c) -> (a -> b) -> a -> c Function composition (/) :: Fractional a => a -> a -> a (/=) :: Eq a => a -> a -> Bool not equal (<) :: Ord a => a -> a -> Bool (<=) :: Ord a => a -> a -> Bool (==) :: Eq a => a -> a -> Bool (>) :: Ord a => a -> a -> Bool (>=) :: Ord a => a -> a -> Bool (>>) :: m a -> m b -> m b Monadic binding (>>=) :: Monad m => m a -> (a -> m b) -> m b Monadic binding (^) :: (Num a. Integral b) => a -> b -> a (^^) :: (Fractional a.Classes class class class class class (Fractional class (Num class class (Real a.2] !! 1 = 1 ($) :: (a -> b) -> a -> b f x $ g y = f x (g y) (&&) :: Bool -> Bool -> Bool Boolean `and' (||) :: Bool -> Bool -> Bool Boolean `or' (*) :: Num a => a -> a -> a (**) :: Floating a => a -> a -> a (+) :: Num a => a -> a -> a (++) :: MonadPlus m => m a -> m a -> m a "abc" ++ "def" = "abdec" (-) :: Num a => a -> a -> a (.1. Enum class class (MonadZero class (Monad class (Eq a. Fractional class Bounded a Enum a Eq a Eval a Floating a Fractional a Functor f Integral a Monad m MonadPlus m MonadZero m Num a Ord a Read a Real a RealFloat a RealFrac a Show a a) => a) => a) => m) m) a) a) => => => => a) => a) => a) => Functions and Methods (!!) :: [a] -> Int -> a [0. Show a. Floating class (Real a.
o] [m. Int) div :: Integral a => a -> a -> a divMod :: Integral a => a -> a -> (a. True] = True any :: (a -> Bool) -> [a] -> Bool any (== 'c') "abc" = True appendFile :: FilePath -> String -> IO () applyM :: Monad m => (a -> m b) -> m a -> m b asTypeOf :: a -> a -> a Sort of a type cast asin :: Floating a => a -> a asinh :: Floating a => a -> a atan :: Floating a => a -> a atan2 :: RealFrac a => a -> a atanh :: Floating a => a -> a break :: (a -> Bool) -> [a] -> ([a].b. Integral b) => a -> b foldl :: (a -> b -> a) -> a -> [b] -> a foldl (+) 0 [a.b..3] = (.5] = [3.all :: (a -> Bool) -> [a] -> Bool all (/= 'a') "cba" = False and :: [Bool] -> Bool and [True."bc". Int) floor :: (RealFrac a..c] = ((0+a)+b)+c foldl1 :: (a -> a -> a) -> [a] -> a foldl1 (+) [a..2. [a]) break (<2) [1. b) -> c) -> a -> b -> c cycle :: [a] -> [a] cycle "abc" = "abcabcabc .] [m.n] a . Integral b) => a -> b compare :: Ord a => a -> a -> Ordering concat :: MonadPlus m => [m a] -> m a concat ["a"..." decodeFloat :: RealFloat a => a -> (Integer.] [m."d"] = "abcd" concatMap :: (a -> [b]) -> [a] -> [b] const :: a -> b -> a cos :: Floating a => a -> a cosh :: Floating a => a -> a curry :: ((a. True.n.c] = (a+b)+c foldr :: (a -> b -> b) -> b -> [a] -> b [n. a) drop :: Int -> [a] -> [a] drop 2 "abcd" = "cd" dropWhile :: (a -> Bool) -> [a] -> [a] dropWhile (>3) [5.5] elem :: Eq a => a -> [a] -> Bool 'a' `Elem` "abc" = True encodeFloat :: RealFloat a => Integer -> Int -> a enumFrom :: Enum a => a -> [a] enumFromThen :: Enum a => a -> a -> [a] enumFromThenTo :: Enum a => a -> a -> a -> [a] enumFromTo :: Enum a => a -> a -> [a] error :: String -> a even :: Integral a => a -> Bool exp :: Floating a => a -> a exponent :: RealFloat a => a -> Int fail :: IOError -> IO a filter :: MonadZero m => (a -> Bool) -> m a -> m filter isUpper "AbCd" = "AB" flip :: (a -> b -> c) -> (b -> a -> c) floatDigits :: RealFloat a => a -> Int floatRadix :: RealFloat a => a -> Integer floatRange :: RealFloat a => a -> (Int.n.3.[2.3]) catch :: IO a -> (IOError -> IO a) -> IO a ceiling :: (RealFrac a..
b. " "." def")] lines :: String -> [String] log :: Floating a => a -> a logBase :: Floating a => a -> a -> a lookup :: Eq a => a -> [(a.. b)] -> Maybe b map :: Functor f => (a -> b) -> f a -> f b mapM :: Monad m => (a -> m b) -> [a] -> m [b] mapM_ :: Monad m => (a -> m b) -> [a] -> m () max :: Ord a => a -> a -> a maxBound :: Bounded a => a maximum :: Ord a => [a] -> a maybe :: b -> (a -> b) -> Maybe a -> b maybe 0 (+1) (Just 1) = 2 min :: Ord a => a -> a -> a minBound :: Bounded a => a minimum :: Ord a => [a] -> a mod :: Integral a => a -> a -> a negate :: Num a => a -> a not :: Bool -> Bool notElem :: Eq a => a -> [a] -> Bool null :: [a] -> Bool odd :: Integral a => a -> Bool or :: [Bool] -> Bool otherwise :: Bool pi :: Floating a => a pred :: Enum a => a -> a .foldr (+) 0 [a. b) -> a gcd :: (Integral a) => a -> a -> a getChar :: IO Char eof generates an IOError getContents :: IO String getLine :: IO Char eof generates an IOError guard :: MonadZero m => Bool -> m () head :: [a] -> a id :: a -> a init :: [a] -> [a] init "abcd" = "abc" interact :: (String -> String) -> IO () isDenormalized :: RealFloat a => a -> Bool isIEEE :: RealFloat a => a -> Bool isInfinite :: RealFloat a => a -> Bool isNaN :: RealFloat a => a -> Bool isNegativeZero :: RealFloat a => a -> Bool iterate :: (a -> a) -> a -> [a] iterate (++ " ") "" = ["".c] = a+(b+(c+0)) foldr1 :: (a -> a -> a) -> [a] -> a foldr (+) [a. " "..c] = a+(b+c) fromEnum :: Enum a => a -> Int fromInteger :: Num a => Integer -> a fromIntegral :: (Integral a..b.] last :: [a] -> a last "abcde" = "e" lcm :: Integral a => a -> a -> a length :: [a] -> Int length "Abc" = 3 lex :: ReadS String lex "abc def" = [("abc". Num b) => a -> b fromRational :: Fractional a => Rational -> a fromRealFrac :: (RealFrac a. Fractional b) => a -> b fst :: (a.
b) -> b span :: (a -> Bool) -> [a] -> ([a]. [a]) span isAlpha "ab cd" = ("ab".3] = [6.2."cdef") sqrt :: Floating a => a -> a .184.108.40.206] = [1.6] scanr :: (a -> b -> b) -> b -> [a] -> [b] scanr (+) 0 [1.5..0] scanr1 :: (a -> a -> a) -> [a] -> [a] scanr (+) [1." replicate :: Int -> a -> [a] replicate 4 'a' = "aaaa" return :: Monad m => a -> m a reverse :: [a] -> [a] reverse "abc" = "cba" round :: (RealFrac a." 2")] readsPrec :: Read a => Int -> ReadS a recip :: Fractional a => a -> a rem :: Integral a => a -> a -> a repeat :: a -> [a] repeat 'a' = "aaaaaaaaa.5.pred True = False print :: Show a => IO () adds a newline product :: Num a => [a] -> a properFraction :: (RealFrac a.3] = [6..3.1] seq :: Eval a => a -> a -> b sequence :: Monad m => [m a] -> m () do operations in sequence show :: Show a => a -> String showChar :: Char -> ShowS showList :: Show a => [a] -> ShowS showParen :: Bool -> ShowS -> ShowS showString :: String -> ShowS shows :: Show a => a -> ShowS showsPrec :: Show a => Int -> a -> ShowS significand :: RealFloat a => a -> a signum :: Num a => a -> a sin :: Floating a => a -> a sinh :: Floating a => a -> a snd :: (a. Integral b) => a -> (b. a) putChar :: Char -> IO () putStr :: String -> IO () putStrLn :: String -> IO () adds a newline quot :: Integral a => a -> a -> a quotRem :: Integral a => a -> a -> (a. a) read :: Read a => String -> a readFile :: FilePath -> IO String readIO :: Read a => String -> IO a fails with IOError readList :: Read a => ReadS [a] readLn :: Read a => IO a readParen :: Bool -> ReadS a -> ReadS a reads :: Read a => ReadS a reads "1 2" :: [(Int.2.1.3] = [1." cd") splitAt :: Int -> [a] -> ([a]. [a]) splitAt 2 "abcdef" = ("ab". Integral b) => a -> b scaleFloat :: RealFloat a => Int -> a -> a scanl :: (a -> b -> a) -> a -> [b] -> [a] scanl (+) 0 [1.String)] = [(1.6] scanl1 :: (a -> a -> a) -> [a] -> [a] scanl1 (+) [1.
('c'. [b]) unzip [('a'. ('b'. Integral b) => a -> b uncurry :: (a -> b -> c) -> ((a. b)] zip "abc" "de" = [('a'. c)] zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] zipWith (+) [1.bd"] unzip3 :: [(a.2] [3. c)] -> ([a].'d')] = ["ac"."as+3"] writeFile :: FilePath -> String -> IO () zero :: MonadZero m => m a zip :: [a] -> [b] -> [(a.e')] zip3 :: [a] -> [b] -> [c] -> [(a.1] =  tan :: Floating a => a -> a tanh :: Floating a => a -> a toEnum :: Enum a => Int -> a toEnum 0 :: Bool = False toInteger :: Integral a => a -> Integer toRational :: Real a => a -> Rational truncate :: (RealFrac a.'d').4] = [4."d". b. [b]. [c]) userError :: String -> IOError words :: String -> [String] words "ab d as+3" = ["ab". b)] -> ([a].7] zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d] .strict :: Eval a => (a -> b) -> (a -> b) subtract :: Num a => a -> a -> a succ :: Enum a => a -> a succ False = True sum :: Num a => [a] -> a sum [1.2. b) -> c) undefined :: a unlines :: [String] -> String until :: (a -> Bool) -> (a -> a) -> a -> a until (> 3) (+ 2) 0 = 4 unwords :: [String] -> String unzip :: [(a.'b').3] = 6 tail :: [a] -> [a] tail "abc" = "bc" take :: Int -> [a] -> [a] take 3 "abcde" = "abc" takeWhile :: (a -> Bool) -> [a] -> [a] takeWhile (> 2) [3.2. b.