Россия, Петерубрг, СПБ-ГПУ, 1998 |
Рациональные числа
module Ratio ( Ratio, Rational, (%), numerator, denominator, approxRational ) where infixl 7 % data (Integral a) => Ratio a = ... type Rational = Ratio Integer (%) :: (Integral a) => a -> a -> Ratio a numerator, denominator :: (Integral a) => Ratio a -> a approxRational :: (RealFrac a) => a -> a -> Rational instance (Integral a) => Eq (Ratio a) where ... instance (Integral a) => Ord (Ratio a) where ... instance (Integral a) => Num (Ratio a) where ... instance (Integral a) => Real (Ratio a) where ... instance (Integral a) => Fractional (Ratio a) where ... instance (Integral a) => RealFrac (Ratio a) where ... instance (Integral a) => Enum (Ratio a) where ... instance (Read a,Integral a) => Read (Ratio a) where ... instance (Integral a) => Show (Ratio a) where ...
Для каждого типа Integral t есть тип Ratio t рациональных пар с компонентами типа t. Имя типа Rational является синонимом для Ratio Integer.
Ratio является экземпляром классов Eq, Ord, Num, Real, Fractional, RealFrac, Enum, Read и Show. В каждом случае экземпляр для Ratio t просто "повышает" соответствующие операции над t. Если t является ограниченным типом, результаты могут быть непредсказуемы; например, Ratio Int может вызвать переполнение целого числа даже для небольших по абсолютной величине рациональных чисел.
Оператор (%) составляет отношение двух целых чисел, сокращая дробь до членов без общего делителя и таких, что знаменатель является положительным. Функции numerator и denominator извлекают компоненты отношения (соответственно числитель и знаменатель дроби); эти компоненты находятся в приведенном виде с положительным знаменателем. Ratio является абстрактным типом. Например, 12 % 8 сокращается до 3/2, а 12 % (-8) сокращается до (-3)/2.
Функция approxRational, будучи примененной к двум действительным дробным числам x и epsilon, возвращает простейшее рациональное число в пределах открытого интервала (x-epsilon, x+epsilon). Говорят, что рациональное число n/d в приведенном виде является более простым, чем другое число n'/d', если |n| >=|n'| и d >=d'. Обратите внимание, что можно доказать, что любой действительный интервал содержит единственное простейшее рациональное число.
12.1. Библиотека Ratio
- Стандартные функции над рациональными числами
module Ratio ( Ratio, Rational, (%), numerator, denominator, approxRational ) where infixl 7 % ratPrec = 7 :: Int data (Integral a) => Ratio a = !a :% !a deriving (Eq) type Rational = Ratio Integer (%) :: (Integral a) => a -> a -> Ratio a numerator, denominator :: (Integral a) => Ratio a -> a approxRational :: (RealFrac a) => a -> a -> Rational
- "reduce" - это вспомогательная функция, которая используется только в этом модуле. Она нормирует отношение путем деления числителя и знаменателя на их наибольший общий делитель.
- Например, 12 `reduce` 8 == 3 :% 2 - 12 `reduce` (-8) == 3 :% (-2) reduce _ 0 = error "Ratio.% : нулевой знаменатель" reduce x y = (x `quot` d) :% (y `quot` d) where d = gcd x y x % y = reduce (x * signum y) (abs y) numerator (x :% _) = x denominator (_ :% y) = y instance (Integral a) => Ord (Ratio a) where (x:%y) <= (x':%y') = x * y' <= x' * y (x:%y) < (x':%y') = x * y' < x' * y instance (Integral a) => Num (Ratio a) where (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:%y) = signum x :% 1 fromInteger x = fromInteger x :% 1 instance (Integral a) => Real (Ratio a) where toRational (x:%y) = toInteger x :% toInteger y instance (Integral a) => Fractional (Ratio a) where (x:%y) / (x':%y') = (x*y') % (y*x') recip (x:%y) = y % x fromRational (x:%y) = fromInteger x :% fromInteger y instance (Integral a) => RealFrac (Ratio a) where properFraction (x:%y) = (fromIntegral q, r:%y) where (q,r) = quotRem x y instance (Integral a) => Enum (Ratio a) where succ x = x+1 pred x = x-1 toEnum = fromIntegral fromEnum = fromInteger . truncate - Может вызвать переполнение enumFrom = numericEnumFrom - Эти функции вида numericEnumXXX enumFromThen = numericEnumFromThen - определены в Prelude.hs enumFromTo = numericEnumFromTo - но не экспортируются оттуда! enumFromThenTo = numericEnumFromThenTo instance (Read a, Integral a) => Read (Ratio a) where readsPrec p = readParen (p > ratPrec) (\r -> [(x%y,u) | (x,s) <- readsPrec (ratPrec+1) r, ("%",t) <- lex s, (y,u) <- readsPrec (ratPrec+1) t ]) instance (Integral a) => Show (Ratio a) where showsPrec p (x:%y) = showParen (p > ratPrec) (showsPrec (ratPrec+1) x . showString " % " . showsPrec (ratPrec+1) y) approxRational x eps = simplest (x-eps) (x+eps) where simplest x y | y < x = simplest y x | x == y = xr | x > 0 = simplest' n d n' d' | y < 0 = - simplest' (-n') d' (-n) d | otherwise = 0 :% 1 where xr@(n:%d) = toRational x (n':%d') = toRational y simplest' n d n' d' - предполагает, что 0 < n%d < n'%d' | r == 0 = q :% 1 | q /= q' = (q+1) :% 1 | otherwise = (q*n''+d'') :% n'' where (q,r) = quotRem n d (q',r') = quotRem n' d' (n'':%d'') = simplest' d' r' d r