These notes include some of the types and functions we discussed in the lecture.
Here is a Haskell datatype for representing rational numbers:
data Frac = Frac Int Int -- numerator, denominator
instance Eq Frac where
(Frac num1 denom1) == (Frac num2 denom2) =
num1 * denom2 == num2 * denom1
instance Ord Frac where
compare (Frac num1 denom1) (Frac num2 denom2) =
compare (num1 * denom2) (num2 * denom1)
instance Show Frac where
show (Frac num denom) = (show num) ++ "/" ++ (show denom)We can generalize our fraction type so that it can use an
arbitrary type (e.g. Integer) in the numerator and
denominator. With this generalized type, we must add class
constraints to our instance declarations:
data GFrac t = GFrac t t -- numerator, denominator
instance (Eq t, Num t) => Eq (GFrac t) where
(GFrac num1 denom1) == (GFrac num2 denom2) =
num1 * denom2 == num2 * denom1
instance (Ord t, Num t) => Ord (GFrac t) where
compare (GFrac num1 denom1) (GFrac num2 denom2) =
compare (num1 * denom2) (num2 * denom1)
instance (Show t) => Show (GFrac t) where
show (GFrac num denom) = (show num) ++ "/" ++ (show denom)Here is a type class representing a functional queue of integers:
class IntQueue q where isEmpty :: q -> Bool enqueue :: Int -> q -> q dequeue :: q -> (Int, q)
Here's a datatype that implements a queue using a list of integers.
newtype LIQueue = LIQueue [Int] instance IntQueue LIQueue where isEmpty (LIQueue q) = null q enqueue i (LIQueue q) = LIQueue (q ++ [i]) dequeue (LIQueue (x : xs)) = (x, LIQueue xs)
Of course this is inefficient: adding N elements to a queue can take O(N2). At the end of the lecture, we sketched how to implement a more efficient functional queue using a pair of lists. We will implement this idea in the Thursday tutorial.