Reputation: 37
I've implemented a normalisation function normaliseRat :: Rat -> Rat for rational numbers so that all of Rat 2 4, Rat (-1) (-2) and Rat 1 2 are transformed into the same internal representation. Furthermore, implemented a function createRat :: Integer -> Integer -> Rat that, given two Integers, returns a normalised Rat. Note: I used the Prelude that contains a function gcd to compute the greatest common divisor of two integers.
gcd' :: Integer -> Integer -> Integer
gcd' a b = if b == 0 then a else gcd' b (a `mod` b)
data Rat = Rat Integer Integer
normaliseRat :: Rat -> Rat
normaliseRat (Rat num den) =
let commonDivisor = gcd' (abs num) (abs den)
num' = num `div` commonDivisor
den' = den `div` commonDivisor
in Rat (if den < 0 then (-num') else num') (abs den')
createRat :: Integer -> Integer -> Rat
createRat num den = normaliseRat (Rat num den)
-- Adding the Show instance for Rat
instance Show Rat where
show (Rat num den) = show num ++ "/" ++ show den
This program is giving the expected result. Like:
ghci>createRat 2 4
ghci> rat1
1/2
Now I want to make Rat an instance of Eq and Ord. Of course, Rat 2 4 == Rat 1 2 should evaluate to True.
Upvotes: -1
Views: 64
Reputation: 153172
One option is to ensure that Rat 2 4
is not a value that can be constructed. Do this by only exposing the part of the API that preserves normalization. For example:
module Rat (
Rat, -- expose the type, but not the data constructor
-- (compare an export of Rat(Rat) or Rat(..) which would export both)
createRat,
)
-- because rationals are always created already-reduced, the
-- derived Eq instance is good enough
data Rat = Rat Integer Integer deriving Eq
createRat :: Integer -> Integer -> Rat
createRat = undefined
-- reduction isn't enough if you want the usual ordering on
-- rationals, so we need to implement this one ourselves
instance Ord Rat where compare (Rat n d) (Rat n' d') = undefined
This is a fairly standard technique, and you can find more resources using "smart constructor" as a search term.
As an aside, I would recommend deriving your Show
instance. If you'd like a way to show rationals that is prettier, export a function specifically for that, named showRat
or similar. This, too, is a common practice.
Upvotes: 2
Reputation: 477533
We know that two fractions a/b and c/d are equal if a×d = c×b (given the denominators are not zero), so we can implement the Eq
instance as:
instance Eq Rat where
Rat a b == Rat c d = a * d == b * c
I leave the instance of Ord
as an exercise. This will be more complicated, since the denominators can be negative as well.
Upvotes: 0