Constructors
new
Based on the decisions I made in Representation I came up with the following requirements for new
.
new 1 2 == Just (Rational 1 2)
new 3 0 == Nothing
new 2 4 == Just (Rational 1 2)
new 3 6 == Just (Rational 1 2)
new 4 8 == Just (Rational 1 2)
new -1 2 == Just (Rational -1 2)
new 1 -2 == Just (Rational -1 2)
new -1 -2 == Just (Rational 1 2)
new -2 4 == Just (Rational -1 2)
new 2 -4 == Just (Rational -1 2)
new -2 -4 == Just (Rational 1 2)
The following implementation satisfies the requirements:
new : Int -> Int -> Maybe Rational
new numer denom =
if denom == 0 then
Nothing
else
Just (makeRational numer denom)
makeRational : Int -> Int -> Rational
makeRational numer denom =
let
divisor =
gcd numer denom
g =
if denom < 0 then
-divisor
else
divisor
n =
numer // g
d =
denom // g
in
Rational n d
gcd : Int -> Int -> Int
gcd a b =
gcdHelper (abs a) (abs b)
gcdHelper : Int -> Int -> Int
gcdHelper a b =
if b == 0 then
a
else
gcdHelper b (modBy b a)
To remove all the common factors from both the numerator and denominator I divide both by their greatest common divisor. gcd
implements the Euclidean algorithm, which is an efficient method for computing the greatest common divisor (GCD) of two integers.
gcdHelper
is implemented using tail-recursion so that it can be optimized into a loop.
I tested that new
met the requirements using elm repl
. The unit tests came later.
Convenient Constructors
Having to deal with Maybe
everytime you need a rational number can become tedious. The zero
and fromInt
constructors make it quite easy to create the rational numbers for zero and the other integers.
zero
zero : Rational
zero =
Rational 0 1
fromInt
fromInt : Rational
fromInt n =
Rational n 1