Опубликован: 19.09.2008 | Доступ: свободный | Студентов: 658 / 70 | Оценка: 4.50 / 5.00 | Длительность: 21:25:00
Лекция 7:

Предопределенные типы и классы

< Лекция 6 || Лекция 7: 123 || Лекция 8 >

6.3.6. Класс Monad

class  Monad m  where
    (>>=)   :: m a -> (a -> m b) -> m b
    (>>)    :: m a -> m b -> m b
    return  :: a -> m a
    fail    :: String -> m a

    m >> k  =  m >>= \_ -> k
    fail s  = error s

Класс Monad определяет основные операции над монадами. Для получения дополнительной информации о монадах смотрите лекцию "Основные операции ввода - вывода" .

"do"-выражения предоставляют удобный синтаксис для записи монадических выражений (см. раздел "Выражения" ). Метод fail вызывается при ошибке сопоставления с образцом в do-выражении.

В Prelude списки, Maybe и IO являются экземплярами класса Monad. Метод fail для списков возвращает пустой список [], для Maybe возвращает Nothing, а для IO вызывает заданное пользователем исключение в монаде IO (см. раздел "Основные операции ввода - вывода" ).

Экземпляры класса Monad должны удовлетворять следующим условиям:

return a >>= k	= 	k a 
m >>= return	= 	m 
m >>= (\x -> k x >>= h)	= 	(m >>= k) >>= h

Экземпляры классов Monad и Functor должны дополнительно удовлетворять условию:

fmap f xs	= 	xs >>= return . f

Все экземпляры класса Monad, определенные в Prelude, удовлетворяют этим условиям.

Prelude обеспечивает следующие вспомогательные функции:

sequence  :: Monad m => [m a] -> m [a] 
sequence_ :: Monad m => [m a] -> m () 
mapM      :: Monad m => (a -> m b) -> [a] -> m [b]
mapM_     :: Monad m => (a -> m b) -> [a] -> m ()
(=<<)     :: Monad m => (a -> m b) -> m a -> m b

6.3.7. Класс Bounded

class  Bounded a  where
    minBound, maxBound :: a

Класс Bounded используется для именования верхней и нижней границ типа. Класс Ord не является суперклассом класса Bounded, так как типы, которые не являются полностью упорядоченными, могут также иметь верхнюю и нижнюю границы. Типы Int, Char, Bool, (), Ordering и все кортежи являются экземплярами класса Bounded. Класс Bounded можно использовать для выведения любого перечислимого типа; minBound является первым в списке конструкторов объявления data, а maxBound - последним. Класс Bounded можно также использовать для выведения типов данных, у которых один конструктор и типы компонентов находятся в Bounded.

6.4. Числа

Haskell предоставляет несколько видов чисел; на числовые типы и операции над ними сильно повлияли Common Lisp и Scheme. Имена числовых функций и операторы обычно перегружены посредством использования нескольких классов типов с отношением включения, которые изображены на рис. "Предопределенные типы и классы" . Класс Num числовых типов является подклассом класса Eq, так как все числа можно сравнить на равенство; его подкласс Real также является подклассом класса Ord, так как остальные операции сравнения применимы ко всем числам, за исключением комплексных (определенных в библиотеке Complex ). Класс Integral содержит целые числа ограниченного и неограниченного диапазона; класс Fractional содержит все нецелые типы; а класс Floating содержит все числа с плавающей точкой, действительные и комплексные.

В Prelude определены только наиболее основные числовые типы: целые числа фиксированной точности (Int), целые числа произвольной точности (Integer), числа с плавающей точкой одинарной точности (Float) и двойной точности (Double). Остальные числовые типы, такие как рациональные и комплексные числа, определены в библиотеках. В частности тип Rational - это отношение двух значений типа Integer, он определен в библиотеке Ratio.

Заданные по умолчанию операции над числами с плавающей точкой, определенные в Haskell Prelude, не соответствуют текущим стандартам независимой от языка арифметики (LIA). Эти стандарты требуют значительно большей сложности в числовой структуре и потому были отнесены к библиотеке. Некоторые, но не все, аспекты стандарта IEEE чисел с плавающей точкой были учтены в классе RealFloat из Prelude.

Стандартные числовые типы перечислены в таблице 6.1. Тип Int целых чисел конечной точности охватывает по меньшей мере диапазон [ - 229, 229 - 1]. Поскольку Int является экземпляром класса Bounded, для определения точного диапазона, заданного реализацией, можно использовать maxBound и minBound. Float определяется реализацией; желательно, чтобы этот тип был по меньшей мере равен по диапазону и точности типу IEEE одинарной точности. Аналогично, тип Double должен охватывать диапазон чисел IEEE двойной точности. Результаты исключительных ситуаций (таких как выход за верхнюю или нижнюю границу) для чисел фиксированной точности не определены; в зависимости от реализации это может быть ошибка ( \perp ), усеченное значение или специальное значение, такое как бесконечность, неопределенность и т.д.

Таблица 6.1. Стандартные числовые типы
Тип Класс Описание
Integer Integral Целые числа произвольной точности
Int Integral Целые числа фиксированной точности
(Integral a) -> Ratio a RealFrac Рациональные числа
Float RealFloat Действительные числа с плавающей точкой одинарной точности
Double RealFloat Действительные числа с плавающей точкой двойной точности
(RealFloat a) -> Complex a Floating Комплексные числа с плавающей точкой

Стандартные классы чисел и другие числовые функции, определенные в Prelude, изображены в примерах. 6.1 - 6.2. На рис. 6.1 показаны зависимости между классами и встроенными типами, которые являются экземплярами числовых классов.

class  (Eq a, Show a) => Num a  where
    (+), (-), (*)  :: a -> a -> a
    negate         :: a -> a
    abs, signum    :: a -> a
    fromInteger    :: Integer -> a

class  (Num a, Ord a) => Real a  where
    toRational ::  a -> Rational

class  (Real a, Enum a) => Integral a  where
    quot, rem, div, mod :: a -> a -> a
    quotRem, divMod     :: a -> a -> (a,a)
    toInteger           :: a -> Integer

class  (Num a) => Fractional a  where
    (/)          :: a -> a -> a
    recip        :: a -> a
    fromRational :: Rational -> a

class  (Fractional a) => Floating a  where
    pi                  :: a
    exp, log, sqrt      :: a -> a
    (**), logBase       :: a -> a -> a
    sin, cos, tan       :: a -> a
    asin, acos, atan    :: a -> a
    sinh, cosh, tanh    :: a -> a
    asinh, acosh, atanh :: a -> a
Листинг 6.1. Стандартные классы чисел и связанные с ними операции, часть 1
class  (Real a, Fractional a) => RealFrac a  where
    properFraction   :: (Integral b) => a -> (b,a)
    truncate, round  :: (Integral b) => a -> b
    ceiling, floor   :: (Integral b) => a -> b

class  (RealFrac a, Floating a) => RealFloat a  where
    floatRadix          :: a -> Integer
    floatDigits         :: a -> Int
    floatRange          :: a -> (Int,Int)
    decodeFloat         :: a -> (Integer,Int)
    encodeFloat         :: Integer -> Int -> a
    exponent            :: a -> Int
    significand         :: a -> a
    scaleFloat          :: Int -> a -> a
    isNaN, isInfinite, isDenormalized, isNegativeZero, isIEEE 
                        :: a -> Bool
    atan2               :: a -> a -> a

gcd, lcm :: (Integral a) => a -> a-> a
(^)      :: (Num a, Integral b) => a -> b -> a
(^^)     :: (Fractional a, Integral b) => a -> b -> a

fromIntegral :: (Integral a, Num b) => a -> b
realToFrac   :: (Real a, Fractional b) => a -> b
Листинг 6.2. Стандартные классы чисел и связанные с ними операции, часть 2

6.4.1. Числовые литералы

Синтаксис числовых литералов описан в разделе "Лексическая структура Haskell 98" . Целые литералы представляет собой применение функции fromInteger к соответствующему значению типа Integer. Аналогично, литералы с плавающей точкой обозначают применение fromRational к значению типа Rational (то есть Ratio Integer ). С учетом заданных типов

fromInteger  :: (Num a) => Integer -> a
fromRational :: (Fractional a) => Rational -> a

целые литералы и литералы с плавающей точкой имеют соответственно тип (Num a) -> a и (Fractional a) -> a. Числовые литералы определены косвенным образом для того, чтобы их можно было рассматривать как значения любого подходящего числового типа. В разделе "Объявления и связывания имен" рассматривается неоднозначность перегрузки.

6.4.2. Арифметические и теоретико-числовые операции

Инфиксные методы класса (+), (*), (-) и унарная функция negate (которая также может быть записана как знак минус, стоящий перед аргументом, см. раздел "Выражения" ) применимы ко всем числам. Методы класса quot, rem, div и mod применимы только к целым числам, тогда как метод класса (/) применим только к дробным. Методы класса quot, rem, div и mod удовлетворяют следующим условиям, если y отличен от нуля:

(x `quot` y)*y + (x `rem` y) == x
(x `div`  y)*y + (x `mod` y) == x

'quot' - это деление нацело с округлением в сторону нуля, тогда как результат 'div' округляется в сторону отрицательной бесконечности. Метод класса quotRem принимает в качестве аргументов делимое и делитель и возвращает пару (частное, остаток); divMod определен аналогично:

quotRem x y  =  (x `quot` y, x `rem` y)
divMod  x y  =  (x `div` y, x `mod` y)

Также для целых чисел определены предикаты even (четный) и odd (нечетный):

even x =  x `rem` 2 == 0
odd    =  not . even

Наконец, имеются функции, которые возвращают наибольший общий делитель и наименьшее общее кратное. gcd x y вычисляет наибольшее (положительное) целое число, которое является делителем и x, и y, например, gcd (-3) 6 = 3, gcd (-3) (-6) = 3, gcd 0 4 = 4. gcd 0 0 вызывает ошибку времени выполнения программы.

lcm x y вычисляет наименьшее положительное целое число, для которого и x, и y являются делителями.

6.4.3. Возведение в степень и логарифмы

Показательная функция exp и логарифмическая функция log принимают в качестве аргумента число с плавающей точкой и используют при вычислении основание e. logBase a x возвращает логарифм x по основанию a. sqrt возвращает арифметическое значение квадратного корня числа с плавающей точкой. Имеются три операции возведения в степень, каждая из которых принимает по два аргумента: возводит любое число в неотрицательную целую степень, ( ) возводит дробное число в любую целую степень и (**) принимает два аргумента с плавающей точкой. Значение x 0 или x 0 равно 1 для любого x, включая ноль; значение 0**y не определено.

6.4.4. Абсолюная величина и знак

Число имеет абсолютную величину и знак. Функции abs и signum применимы к любому числу и удовлетворяют условию:

abs x * signum x == x

Для действительных чисел эти функции определены следующим образом:

abs x    | x >= 0  = x
             | x < 0  = -x

signum x | x >0  = 1
               | x == 0  = 0
               | x <  0  = -1

6.4.5. Тригонометрические функции

Класс Floating предоставляет функции для вычисления кругового и гиперболического синуса, косинуса, тангенса и обратных функций. Имеются реализации tan, tanh, logBase, ** и sqrt, заданные по умолчанию, но разработчики могут реализовать свои, более точные функции.

Класс RealFloat предоставляет версию функции для вычисления арктангенса, которая принимает два действительных аргумента с плавающей точкой. Для действительных чисел с плавающей точкой x и y atan2 y x вычисляет угол (от положительной оси X) вектора, проведенного из начала координат в точку (x,y) . atan2 y x возвращает значение в диапазоне [-pi, pi]. При этом, в соответствии с семантикой Common Lisp для начала координат, поддерживются нули со знаком. atan2 y 1, где y находится в типе RealFloat, должен вернуть то же самое значение, что и atan y. Имеется заданное по умолчанию определение atan2, но разработчики могут реализовать свою, более точную функцию.

Точное определение вышеупомянутых функций такое же, как и в Common Lisp, которое, в свою очередь, соответствует предложению Пенфилда (Penfield) для APL [9]. Для подробного обсуждения ветвей, разрывностей и реализации смотрите эти ссылки.

6.4.6. Приведение и извлечение компонент

Каждая из функций ceiling, floor, truncate и round принимает в качестве аргумента действительное дробное число и возвращает целое число. ceiling x возвращает наименьшее целое число, которое не меньше чем x, floor x возвращает наибольшее целое число, которое не больше чем x. truncate x возвращает ближайшее к x целое число, которое находится между 0 и x включительно. round x возвращает ближайшее к x целое число, результат округляется в сторону четного числа, если x находится на одинаковом расстоянии от двух целых чисел.

Функция properFraction принимает в качестве аргумента действительное дробное число x и возвращает пару (n,f), такую, что x = n+f, где n - целое число с тем же знаком, что и x, f - дробное число с тем же типом и знаком, что и x, и с абсолютным значением меньше 1. Функции ceiling, floor, truncate и round можно определить в терминах properFraction.

Имеются две функции, которые осуществляют преобразование чисел к типу Rational: toRational возвращает рациональный эквивалент действительного аргумента с полной точностью; approxRational принимает два действительных дробных аргумента x и varepsilon и возвращает простейшее рациональное число, которое отличается от x не более чем на varepsilon, где рациональное число p/q, находящееся в приведенном виде, считается более простым, чем другое число p' /q ', если |p | <- |p ' | и q <- q'. Каждый действительный интервал содержит единственное простейшее рациональное число, в частности, обратите внимание, что 0/1 является простейшим рациональным числом из всех.

Методы класса RealFloat предоставляют эффективный, машинонезависимый способ получить доступ к компонентам числа с плавающей точкой. Функции floatRadix, floatDigits и floatRange возвращают параметры типа с плавающей точкой: соответственно основание числового представления, количество цифр этого основания в мантиссе (значащей части числа) и наибольшее и наименьшее значения, которое может принимать экспонента. Функция decodeFloat, будучи примененной к действительному числу с плавающей точкой, возвращает мантиссу в виде числа типа Integer и соответствующую экспоненту (в виде числа типа Int). Если decodeFloat x возвращает (m,n), то x равно по значению mbn, где b - основание с плавающей точкой, и, кроме того, либо m и n равны нулю, либо bd-1<=m<bd, где d - значение floatDigits x. encodeFloat выполняет обратное преобразование. Функции significand и exponent вместе предоставляют ту же информацию, что и decodeFloat, но более точную, чем Integer, significand x возвращает значение того типа, что и x, но лежащее в пределах открытого интервала (-1,1). exponent 0 равно нулю. scaleFloat умножает число с плавающей точкой на основание, возведенное в целую степень.

Функции isNaN, isInfinite, isDenormalized, isNegativeZero и isIEEE поддерживают числа, представимые в соответствии со стандартом IEEE. Для чисел с плавающей точкой, не соответствующих стандарту IEEE, они могут вернуть ложное значение.

Также имеются следующие функции приведения:

fromIntegral :: (Integral a, Num b)    => a -> b
realToFrac   :: (Real a, Fractional b) => a -> b
< Лекция 6 || Лекция 7: 123 || Лекция 8 >