Как отношение Ratio реализовано в Haskell?
Я кое-что немного смутил, и я не уверен, как я могу больше узнать об этом. Скажем, у меня есть следующая программа:
main :: IO ()
main = do
x <- liftM read getLine
y <- liftM read getLine
print (x % y)
Если я запустил это с помощью ввода 6
и 2
, он напечатает 3 % 1
.
В какой момент происходит упрощение (а именно деление на gcd)? Он реализован в show
? Если это так, то это основное представление рационального еще 6 % 2
? Если нет, то делает ли (%)
упрощение? У меня создалось впечатление, что (%)
является конструктором данных, поэтому как конструктор данных может сделать что-то большее, чем "построить"? Что еще более важно, как я мог бы делать подобные вещи с моими собственными конструкторами данных?
Я ценю любую помощь по этой теме.
Ответы
Ответ 1
Ratio
фактически реализуется в GHC.Real
(очевидно, на GHC) и определяется как
data Ratio a = !a :% !a deriving (Eq)
Чешуйки просто для строгости. Как вы можете видеть, функция %
не является конструктором данных, но :%
is. Поскольку вы не должны напрямую строить Ratio
, вы используете функцию %
, которая вызывает сокращение.
reduce :: (Integral a) => a -> a -> Ratio a
{-# SPECIALISE reduce :: Integer -> Integer -> Rational #-}
reduce _ 0 = ratioZeroDenominatorError
reduce x y = (x `quot` d) :% (y `quot` d)
where d = gcd x y
(%) :: (Integral a) => a -> a -> Ratio a
x % y = reduce (x * signum y) (abs y)
Правило состоит в том, что если оператор начинает с двоеточия :
, то он является конструктором, иначе он является просто нормальным оператором. На самом деле, это часть стандарта Haskell, все операторы типа должны иметь двоеточие в качестве своего первого символа.
Ответ 2
Вы можете просто посмотреть источник , чтобы убедиться сами:
instance (Integral a) => Num (Ratio a) where
(x:%y) + (x':%y') = reduce (x*y' + x'*y) (y*y')
(x:%y) - (x':%y') = reduce (x*y' - x'*y) (y*y')
(x:%y) * (x':%y') = reduce (x * x') (y * y')
negate (x:%y) = (-x) :% y
abs (x:%y) = abs x :% y
signum (x:%_) = signum x :% 1
fromInteger x = fromInteger x :% 1
reduce :: (Integral a) => a -> a -> Ratio a
reduce _ 0 = ratioZeroDenominatorError
reduce x y = (x `quot` d) :% (y `quot` d)
where d = gcd x y