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

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

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

6.3. Стандартные классы Haskell

На рис. 6.1 изображена иерархия классов Haskell , определенных в Prelude, и типы из Prelude, которые являются экземплярами этих классов.

Стандартные классы Haskell

Рис. 6.1. Стандартные классы Haskell

Для многих методов в стандартных классах предусмотрены заданные по умолчанию объявления методов класса (раздел "Объявления и связывания имен" ). Комментарий, данный для каждого объявления class в лекции "Стандартное начало (Prelude)" , определяет наименьшую совокупность определений методов, которые вместе с заданными по умолчанию объявлениями обеспечивают разумное определение для всех методов класса. Если такого комментария нет, то для того, чтобы полностью определить экземпляр, должны быть заданы все методы класса.

6.3.1. Класс Eq

class  Eq a  where
        (==), (/=)  ::  a -> a -> Bool

        x /= y  = not (x == y)
        x == y  = not (x /= y)

Класс Eq предоставляет методы для сравнения на равенство (==) и неравенство (/=). Все основные типы данных, за исключением функций и IO, являются экземплярами этого класса. Экземпляры класса Eq можно использовать для выведения любого определяемого пользователем типа данных, чьи компоненты также являются экземплярами класса Eq.

Это объявление задает используемые по умолчанию объявления методов /= и ==, каждый из которых определен в терминах другого. Если объявление экземпляра класса Eq не содержит описания ни одного из перечисленных методов, тогда оба метода образуют петлю. Если определен один из методов, то другой, заданный по умолчанию метод, будет использовать тот, который определен. Если оба метода определены, заданные по умолчанию методы использоваться не будут.

6.3.2. Класс Ord

class  (Eq a) => Ord a  where
    compare              :: a -> a -> Ordering
    (<), (<=), (>=), (>) :: a -> a -> Bool
    max, min             :: a -> a -> a

    compare x y | x == y    = EQ
                | x <= y    = LT
                | otherwise = GT

    x <= y  = compare x y /= GT
    x <  y  = compare x y == LT
    x >= y  = compare x y /= LT
    x >  y  = compare x y == GT

    - Заметьте, что (min x y, max x y) = (x,y) или (y,x)
    max x y | x <= y    =  y
            | otherwise =  x
    min x y | x <= y    =  x
            | otherwise =  y

Класс Ord используется для полностью упорядоченных типов данных. Все основные типы данных, за исключением функций, IO и IOError, являются экземплярами этого класса. Экземпляры класса Ord можно использовать для выведения любого определяемого пользователем типа данных, чьи компоненты находятся в Ord. Объявленный порядок конструкторов в объявлении данных определяет порядок в производных экземплярах класса Ord. Тип данных Ordering позволяет использовать единообразное сравнение для определения точного порядка двух объектов.

Заданные по умолчанию объявления позволяют пользователю создавать экземпляры класса Ord посредством функции compare с определенным типом или функций == и \gets с определенным типом.

6.3.3. Классы Read и Show

type  ReadS a = String -> [(a,String)]
type  ShowS   = String -> String

class  Read a  where
    readsPrec :: Int -> ReadS a
    readList  :: ReadS [a]
    - ... объявление readList по умолчанию дано в Prelude

class  Show a  where
    showsPrec :: Int -> a -> ShowS
    show      :: a -> String 
    showList  :: [a] -> ShowS

    showsPrec _ x s   = show x ++ s
    show x            = showsPrec 0 x ""
    - ... объявление для showList по умолчанию дано в Prelude

Классы Read и Show используются для преобразования значений к типу строка или преобразования строк к другим значениям. Аргумент типа Int в функциях showsPrec и readsPrec задает приоритет внешнего контекста (см. раздел "Спецификация производных экземпляров" ).

showsPrec и showList возвращают функцию, действующую из String в String, которая обеспечивает постоянную конкатенацию их результатов посредством использования композиции функций. Также имеется специализированный метод show, который использует нулевой приоритет контекста и возвращает обычный String. Метод showList предназначен для того, чтобы предоставить программисту возможность задать специализированный способ представления списков значений. Это особенно полезно для типа Char, где значения типа String должны быть представлены в двойных кавычках, а не в квадратных скобках.

Производные экземпляры классов Read и Show копируют стиль, в котором объявлен конструктор: для ввода и вывода используются инфиксные конструкторы и имена полей. Строки, порождаемые showsPrec, обычно могут быть прочитаны readsPrec.

Все типы Prelude, за исключением функциональных типов и типов IO, являются экземплярами классов Show и Read. (Если желательно, программист может легко сделать функции и типы IO (пустыми) экземплярами класса Show, обеспечив объявление экземпляра.)

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

reads   :: (Read a) => ReadS a
reads   =  readsPrec 0

shows   :: (Show a) => a -> ShowS
shows   =  showsPrec 0

read    :: (Read a) => String -> a
read s  =  case [x | (x,t) <- reads s, ("","") <- lex t] of
              [x] -> x
              []  -> error "PreludeText.read: нет разбора"
              _   -> error "PreludeText.read: неоднозначный разбор"

shows и reads используют заданный по умолчанию нулевой приоритет. Функция read считывает ввод из строки, которая должна быть полностью потреблена процессом ввода.

Функция lex :: ReadS String, используемая функцией read, также является частью Prelude. Она считывает из ввода одну лексему, игнорируя пробельные символы перед лексемой, и возвращает символы, которые составляют лексему. Если входная строка содержит только пробельные символы, lex возвращает одну успешно считанную "лексему", состоящую из пустой строки. (Таким образом lex "" = [("","")].) Если в начале входной строки нет допустимой лексемы, lex завершается с ошибкой (т.е. возвращает []).

6.3.4. Класс Enum

class  Enum a  where
    succ, pred     :: a -> a
    toEnum         :: Int -> a
    fromEnum       :: a -> Int
    enumFrom       :: a -> [a]            - [n..]
    enumFromThen   :: a -> a -> [a]       - [n,n'..]
    enumFromTo     :: a -> a -> [a]       - [n..m]
    enumFromThenTo :: a -> a -> a -> [a]  - [n,n'..m]

    - Заданные по умолчанию объявления даны в Prelude

Класс Enum определяет операции над последовательно упорядоченными типами. Функции succ и pred возвращают соответственно последующий и предшествующий элемент заданного значения. Функции fromEnum и toEnum преобразуют соответственно значения типа Enum к типу Int и значения типа Int к типу Enum. Методы, начинающиеся с enumFrom ..., используются при преобразовании арифметических последовательностей (раздел "Выражения" ).

Экземпляры класса Enum можно использовать для выведения любого перечислимого типа (типы, чьи конструкторы не имеют полей), см. лекцию "Спецификация производных экземпляров" .

Для любого типа, который является экземпляром класса Bounded, а также экземпляром класса Enum, должны выполняться следующие условия:

  • Вызовы succ maxBound и pred minBound должны завершаться с ошибкой времени выполнения программы.
  • fromEnum и toEnum должны завершаться с ошибкой времени выполнения программы, если значение результата не представимо в указанном типе результата. Например, toEnum 7 :: Bool является ошибкой.
  • enumFrom и enumFromThen должны быть определены с неявным указанием границы, так:
    enumFrom     x   = enumFromTo     x maxBound
      enumFromThen x y = enumFromThenTo x y bound
        where
          bound | fromEnum y >= fromEnum x = maxBound
                | otherwise                = minBound

Следующие типы Prelude являются экземплярами класса Enum:

  • Перечислимые типы: (), Bool и Ordering. Семантика этих экземпляров описана в лекции "Спецификация производных экземпляров" . Например, [LT..] - список [LT,EQ,GT] .
  • Char: экземпляр описан в лекции "Стандартное начало (Prelude)" , базируется на примитивных функциях, которые осуществляют преобразование между Char и Int. Например, enumFromTo 'a' 'z' обозначает список строчных букв в алфавитном порядке.
  • Числовые типы: Int, Integer, Float, Double. Семантика этих экземпляров описана далее.

Для всех четырех числовых типов succ добавляет 1, а pred вычитает 1. Преобразования fromEnum и toEnum осуществляют преобразование между заданным типом и типом Int. В случае Float и Double цифры после точки могут быть потеряны. Что вернет fromEnum, будучи примененной к значению, которое слишком велико для того, чтобы уместиться в Int, - зависит от реализации.

Для типов Int и Integer функции перечисления имеют следующий смысл:

  • Последовательность enumFrom e1 - это список [e1,e1+1,e1+2,...].
  • Последовательность enumFromThen e1 e2 - это список [e1,e1+i,e1+2i,...], где приращение i равно e2-e1. Приращение может быть нулевое или отрицательное. Если приращение равно нулю, все элементы списка совпадают.
  • Последовательность enumFromTo e1 e3 - это список [e1,e1+1,e1+2,...e3]. Список пуст, если e1 > e3.
  • Последовательность enumFromThenTo e1 > e3 - это список [e1,e1+i,e1+2i,...e3], где приращение i равно e2-e1. Если приращение является положительным или нулевым, список заканчивается, когда следующий элемент будет больше чем e3 ; список пуст, если e1 > e3. Если приращение является отрицательным, список заканчивается, когда следующий элемент будет меньше чем e3 ; список пуст, если e1 < e3.

Для Float и Double семантика семейства функций enumFrom задается с помощью правил, описанных выше для Int, за исключением того, что список заканчивается, когда элементы станут больше чем e3+i/2 для положительного приращения i или когда они станут меньше чем e3+i/2 для отрицательного i.

Для всех четырех числовых типов из Prelude все функции семейства enumFrom являются строгими по всем своим параметрам.

6.3.5. Класс Functor

class  Functor f  where
    fmap    :: (a -> b) -> f a -> f b

Класс Functor используется для типов, для которых можно установить соответствие (задать отображение). Списки, IO и Maybe входят в этот класс.

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

fmap id	= 	id
fmap (f . g)	= 	fmap f . fmap g

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

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