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

Операции индексации

< Лекция 15 || Лекция 16 || Лекция 17 >
Аннотация: В этой лекции дается описание класса и библиотеки Ix, которая в основном используется для описания массивов

module Ix ( Ix(range, index, inRange, rangeSize) ) where

class  Ord a => Ix a  where
    range       :: (a,a) -> [a]
    index       :: (a,a) -> a -> Int
    inRange     :: (a,a) -> a -> Bool
    rangeSize   :: (a,a) -> Int

instance                   Ix Char      where ...
instance                   Ix Int       where ...
instance                   Ix Integer   where ...
instance  (Ix a, Ix b)  => Ix (a,b)     where ...
- и так далее
instance                   Ix Bool      where ...
instance                   Ix Ordering  where ...

Класс Ix используется для того, чтобы отобразить непрерывный отрезок значений на тип целых чисел. Это используется прежде всего для индексации массивов (см. лекцию "16" ). Класс Ix содержит методы range, index и inRange. Операция index отображает пару ограничений, которая определяет нижнюю и верхнюю границы диапазона, и индекс в целое число. Операция range перечисляет все индексы; операция inRange сообщает, находится ли конкретный индекс в диапазоне, заданном парой ограничений.

Реализация имеет право предполагать выполнение следующих правил относительно этих операций:

range (l,u) !! index (l,u) i == i   - когда i находится в указанном диапазоне
   inRange (l,u) i == i `elem` range (l,u)
   map index (range (l,u))      == [0..rangeSize (l,u)]

15.1. Выведение экземпляров Ix

Есть возможность вывести (произвести) экземпляр класса Ix автоматически, используя инструкцию deriving в объявлении data (раздел "4.3.3" ). Объявления таких производных экземпляров класса Ix возможны только для перечислений (т.е. типов данных, имеющих конструкторы без аргументов) и типов данных с одним конструктором, у которого компоненты имеют типы, являющиеся экземплярами класса Ix. Реализация Haskell должна обеспечить экземпляры класса Ix для кортежей по меньшей мере вплоть до 15 размера.

  • Для перечисления предполагается, что конструкторы без аргументов нумеруются слева направо индексами от 0 до n-1 включительно. Это та же самая нумерация, которая определена в классе Enum. Например, при типе данных:
    data Colour = Red | Orange | Yellow | Green | Blue | Indigo | Violet

    мы получили бы:

    range   (Yellow,Blue)        ==  [Yellow,Green,Blue]
    index   (Yellow,Blue) Green  ==  1
    inRange (Yellow,Blue) Red    ==  False
  • Для типов данных с одним конструктором объявления производных экземпляров являются такими, как те, что изображены на рис. 15.1 для кортежей.
    instance  (Ix a, Ix b)  => Ix (a,b) where
            range ((l,l'),(u,u'))
                    = [(i,i') | i <- range (l,u), i' <- range (l',u')]
            index ((l,l'),(u,u')) (i,i')
                    =  index (l,u) i * rangeSize (l',u') + index (l',u') i'
            inRange ((l,l'),(u,u')) (i,i')
                    = inRange (l,u) i && inRange (l',u') i'
    
    - Экземпляры для остальных кортежей получены по этой схеме:
    -
    -  instance  (Ix a1, Ix a2, ... , Ix ak) => Ix (a1,a2,...,ak)  where
    -      range ((l1,l2,...,lk),(u1,u2,...,uk)) =
    -          [(i1,i2,...,ik) | i1 <- range (l1,u1),
    -                            i2 <- range (l2,u2),
    -                            ...
    -                            ik <- range (lk,uk)]
    -
    -      index ((l1,l2,...,lk),(u1,u2,...,uk)) (i1,i2,...,ik) =
    -        index (lk,uk) ik + rangeSize (lk,uk) * (
    -         index (lk-1,uk-1) ik-1 + rangeSize (lk-1,uk-1) * (
    -          ...
    -           index (l1,u1)))
    -
    -      inRange ((l1,l2,...lk),(u1,u2,...,uk)) (i1,i2,...,ik) =
    -          inRange (l1,u1) i1 && inRange (l2,u2) i2 &&
    -              ... && inRange (lk,uk) ik
    Листинг 15.1. Выведение экземпляров класса Ix

15.2. Библиотека Ix

module Ix ( Ix(range, index, inRange, rangeSize) ) where

class  Ord a => Ix a  where
    range     :: (a,a) -> [a]
    index     :: (a,a) -> a -> Int
    inRange   :: (a,a) -> a -> Bool
    rangeSize :: (a,a) -> Int

    rangeSize b@(l,h) | null (range b) = 0
                      | otherwise      = index b h + 1 
- NB: замена "null (range b)" на "not (l <= h)"
- завершится неудачей, если границы являются кортежами. Например,
-  (1,2) <= (2,1)
- но диапазон (range) тем не менее пуст:
- range ((1,2),(2,1)) = []

instance  Ix Char  where
    range (m,n) = [m..n]
    index b@(c,c') ci
        | inRange b ci  =  fromEnum ci - fromEnum c
        | otherwise     =  error "Ix.index: Индекс находится за пределами диапазона."
    inRange (c,c') i    =  c <= i && i <= c'

instance  Ix Int  where
    range (m,n) = [m..n]
    index b@(m,n) i
        | inRange b i   =  i - m
        | otherwise     =  error "Ix.index: Индекс находится за пределами диапазона."
    inRange (m,n) i     =  m <= i && i <= n

instance  Ix Integer  where
    range (m,n) = [m..n]
    index b@(m,n) i
        | inRange b i   =  fromInteger (i - m)
        | otherwise     =  error "Ix.index: Индекс находится за пределами диапазона."
    inRange (m,n) i     =  m <= i && i <= n

instance (Ix a,Ix b) => Ix (a, b) - является производным, для всех кортежей
instance Ix Bool                  - является производным
instance Ix Ordering              - является производным
instance Ix ()                    - является производным
< Лекция 15 || Лекция 16 || Лекция 17 >