Опубликован: 19.09.2008 | Уровень: специалист | Доступ: платный
Лекция 13:

Рациональные числа

< Лекция 12 || Лекция 13 || Лекция 14 >
Аннотация: Haskell обладает богатой коллекцией численных типов. Стандартные типы включают целые фиксированного и произвольного диапазона, рациональные числа, сформированные для каждого типа целых, вещественные одинарной и двойной точности и комплексные с плавающей точкой. Мы опишем в этой лекции рациональные числа, которые включают в себя библиотеку Ratio и заодно рассмотри ее

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
< Лекция 12 || Лекция 13 || Лекция 14 >
KroshkaRu KroshkaRu
KroshkaRu KroshkaRu
Россия, Петерубрг, СПБ-ГПУ, 1998
Петр Бондареко
Петр Бондареко
Россия