Язык и библиотеки Haskell 98

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

Показывать лекцию целиком
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.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 ()                    - является производным
    Вернуться к учебному плану