我为有理数实现了归一化函数 normaliseRat :: Rat -> Rat,以便所有 Rat 2 4、Rat (-1) (-2) 和 Rat 1 2 都转换为相同的内部表示。此外,还实现了一个函数 createRat :: Integer -> Integer -> Rat,给定两个 Integers,返回一个标准化的 Rat。 注意:我使用包含函数 gcd 的 Prelude 来计算两个整数的最大公约数。
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
这个程序给出了预期的结果。喜欢:
ghci>createRat 2 4
ghci> rat1
1/2
现在我想让 Rat 成为 Eq 和 Ord 的实例。当然,Rat 2 4 == Rat 1 2 的计算结果应为 True。
一种选择是确保
Rat 2 4
不是可以构造的值。通过仅公开 API 中保留规范化的部分来实现此目的。例如:
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
这是一种相当标准的技术,您可以使用“智能构造函数”作为搜索词找到更多资源。
顺便说一句,我建议派生您的
Show
实例。如果您想要一种更漂亮的有理数显示方式,请专门为此导出一个函数,名为 showRat
或类似函数。这也是一种常见做法。
我们知道,如果 a×d = c×b(假设分母不为零),两个分数 a/b 和 c/d 相等,因此我们可以将
Eq
实例实现为:
instance Eq Rat where
Rat a b == Rat c d = a * d == b * c
我将
Ord
的实例留作练习。这会更复杂,因为分母也可以是负数。