COMPUTER SCIENCE 123 Foundations of Computer Science 6. Tuples Summary: This lecture introduces tuples in Haskell. Reference: Thompson Sections 5.1–2 © R.L. While, 2000–3 Tuples • • Most data comes with a structure − e.g. amounts of money are expressed in dollars and cents, i.e. a pair of numbers In Haskell, this could be expressed as a 2-tuple (or a pair) (4, 73) • • A tuple is a sequence of two or more values of any type enclosed in brackets and separated by commas − the type of a tuple is determined by the types of its components For example: (4, 73) (3, True) (25, "isn't", 's') ("", 1999) ("", "1999") • • :: :: :: :: :: (Int, Int) (Int, Bool) (Int, String, Char) (String, Int) (String, String) Note that each of these values has a different type − the word “tuples” refers to a group of types, not a single type Also note that the syntax for the values and for the types is identical − in each case, the brackets and commas match cs123 Foundations of CS 1 of 11 6. Tuples Tuple operations • • • The only thing we can do with a tuple is to take it apart! − of course, once we have taken it apart, we can process the components using all of the usual operations We dissect a tuple by placing it on the left-hand side of an equation and naming the components − this is known technically as pattern-matching Consider a function addCash that adds together two amounts of money − e.g. addCash (2, 85) (1, 99) = (4, 84) addCash :: (Int, Int) -> (Int, Int) -> (Int, Int) -- addCash x y returns the normalised sum -- of cash amounts x and y addCash (x, x') (y, y') = (x + y + z `div` 100, z `mod` 100) where z = x' + y' • Consider a function mulCash that multiplies amounts of money − e.g. mulCash (2, 99) 4 = (11, 96) mulCash :: (Int, Int) -> Int -> (Int, Int) -- mulCash x k returns the normalised -- product of cash amount x and k mulCash (x, x') k = (x * k + z `div` 100, z `mod` 100) where z = x' * k • Note that the first argument to each of these functions has the type (Int, Int) − we can’t call either of them with (3, True) or (25, "isn't", 's') or ("", 1999) or ("", "1999") or … cs123 Foundations of CS 2 of 11 6. Tuples Tuples containing tuples • • A tuple can contain values of any type − including other tuples For example: (4, 9, 0) :: (Int, Int, Int) (4, (9, 0)) :: (Int, (Int, Int)) ((4, 9), 0) :: ((Int, Int), Int) (3, True, "/", "[a]") :: (Int, Bool, String, String) ((3, True), "/", "[a]") :: ((Int, Bool), String, String) (3, (True, "/"), "[a]") :: (Int, (Bool, String), String) (3, True, ("/", "[a]")) :: (Int, Bool, (String, String)) ((3, True), ("/", "[a]")) :: ((Int, Bool), (String, String)) ((3, True, "/"), "[a]") :: ((Int, Bool, String), String) (3, (True, "/", "[a]")) :: (Int, (Bool, String, String)) • • • Again, each of these values has a different type Again note that the syntax for the values and for the types is identical − in each case, the brackets and commas match Exercise: write down the other four tuple-types that contain the same values as the seven above cs123 Foundations of CS 3 of 11 6. Tuples Examples • Consider a function addMarks that takes a name and a set of assessment marks and calculates a final mark addMarks :: (String, (Int, Int, Int)) -> (String, Int) -- addMarks (n, as) returns a -- final mark for n addMarks (n, (a1, a2, ex)) = (n, a1 + a2 + ex) • Or suppose we add the rule that a student fails overall if they fail to submit any piece of the assessment addMarks :: (String, (Int, Int, Int)) -> (String, Int) -- addMarks (n, as) returns a -- final mark for n, if n worked hard addMarks (n, (a1, a2, ex)) | a1==0 || a2==0 || ex==0 = (n, 0) | otherwise = (n, a1+a2+ex) • Tuples can be taken apart in local definitions mark :: (String, (Int, Int, Int)) -> Int -- mark (n, as) returns n's mark mark z = m where (n', m) = addMarks z cs123 Foundations of CS 4 of 11 6. Tuples Type synonyms • • • • Readability is the key to good programming − the easier and more quickly someone else can understand your program, the better One important aspect is your choice of names − the name of a function should always reflect what the function does Another key aspect is the names of types − e.g. what does (Int, Int) mean? Haskell allows us to rename a type using a type synonym declaration − for example type Cash = (Int, Int) type type type type • • Name Marks StudentEntry StudentTotal = = = = String (Int, Int, Int) (Name, Marks) (Name, Int) This is almost always a good idea The type declarations of the functions from above become addCash :: Cash -> Cash -> Cash mulCash :: Cash -> Int -> Cash addMarks :: StudentEntry -> StudentTotal • Their specifications and equations are unchanged cs123 Foundations of CS 5 of 11 6. Tuples An extended example—rational numbers • A rational number is a fraction a b − often a vulgar fraction, i.e. a > e.g. • • 1 2 32 31 22 7 − 19 99 − 9 1 b 0 1 Rational numbers are used in some languages for doing exact (or infinite-precision) arithmetic − with a rational representation, all arithmetic is performed exactly, and only output involves any inaccuracy − using a floating-point representation for arithmetic can lead to unpredictable inaccuracies A rational number can be represented as a pair of integers type Rat = (Int, Int) cs123 Foundations of CS 6 of 11 6. Tuples Normalisation • A normalised rational number obeys three rules − the denominator is positive − a negative rational − a b is represented as e.g. − 0 − 19 99 = −a b −19 99 is represented in the form 0 1 zeroRat :: Rat -- zeroRat returns normalised 0 zeroRat = (0, 1) − it is in its lowest terms, − i.e. a and b have no common factors a b e.g. • a = b 18 20 z where z = gcd(a, b) z = 9 10 In Haskell: normaliseRat :: Rat -> Rat -- normaliseRat r returns normalised r normaliseRat (a, b) | a == 0 = zeroRat | a /= 0 = (a * signum b `div` z, b * signum b `div` z) where z = gcd a b − − − the guards take care of 0 dividing top and bottom by gcd a b ensures that the Rat is in its lowest terms multiplying top and bottom by signum b ensures that the denominator is positive cs123 Foundations of CS 7 of 11 6. Tuples Adding and subtracting rational numbers • Addition is defined by the equation a b • + c d = ad + bc bd In Haskell: addRat :: Rat -> Rat -> Rat -- addRat r r' returns r + r' addRat (a, b) (c, d) = normaliseRat (a * d + b * c, b * d) • Subtraction is defined by the equation a b • − c d = ad − bc bd In Haskell: subRat :: Rat -> Rat -> Rat -- subRat r r' returns r - r' subRat (a, b) (c, d) = normaliseRat (a * d - b * c, b * d) cs123 Foundations of CS 8 of 11 6. Tuples Multiplying and dividing rational numbers • Multiplication is defined by the equation a b • × c d = ac bd In Haskell: mulRat :: Rat -> Rat -> Rat -- mulRat r r' returns r * r' mulRat (a, b) (c, d) = normaliseRat (a * c, b * d) • Division is defined by the equation a b • ÷ c d = ad bc − of course the divisor must not be zero In Haskell: divRat :: Rat -> Rat -> Rat -- pre: r' isn't zero -- divRat r r' returns r / r' divRat (a, b) (c, d) = normaliseRat (a * d, b * c) cs123 Foundations of CS 9 of 11 6. Tuples Comparing rational numbers • Equality is defined by the equation a b • c d == iff == ad bc In Haskell: eqRat :: Rat -> Rat -> Bool -- eqRat r r' returns r == r' eqRat (a, b) (c, d) = a * d == b * c • Orderings are defined by the equation a b • < c d iff ad < bc In Haskell: ltRat :: Rat -> Rat -> Bool -- ltRat r r' returns r < r' ltRat (a, b) (c, d) = a * d < b * c cs123 Foundations of CS 10 of 11 6. Tuples Displaying rational numbers • If we just apply show to a Rat, it will appear in tuple notation Prelude> show zeroRat "(0,1)" (132 reductions, 229 cells) • Better is to define a function showRat that returns a string using the a / b format − or just a for integer Rats showRat :: Rat -> String -- showRat r returns a string containing r showRat (a, b) | b == 1 = show a | b > 1 = show a ++ " / " ++ show b cs123 Foundations of CS 11 of 11 6. Tuples