Операции индексации
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 () - является производным