Руководство разработчика

Контейнерные типы

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

Количество элементов в контейнере может изменяться во время выполнения программы. Максимальное количество элементов контенйнера ограничено только доступной памятью. Контейнер не содержащий ни одного элемента называется пустым. Значение по умолчанию для контейнерного типа – пустой контейнер.

Количество индексов называется размерностью контейнера. Т.е. контейнер с одним индексом – одномерный, с двумя – двумерный и т.д.

Примеры

Объявление переменной контейнерного типа с элементами типа дата и одним строковым индексом:

ПЕРЕМ ДАТА: даты[СТРОКА]

Объявление двумерной переменной с неопределенным типом элемента и строковыми индексами:

ПЕРЕМ *: конт[СТРОКА, СТРОКА]

Элемент контейнера доступен как для получения значения, так и для изменения. Для доступа к элементу контейнера используются квадратные скобки, внутри которых указываются значения индексов через запятую. Если для указанных значений индексов не существует элемента, то он автоматически создается со значением по умолчанию, соответствующим типу элемента. Проверка существования элемента контейнера и удаление элементов возможно с помощью соответствующих операций, а также оператора удаления и оператора СБРОС, который удаляет все элементы.

Пример

Доступ к значениям контейнера даты:

даты["Новый год"] = 01.01.2015 // новый элемент с установкой значения
ПЕРЕМ ДАТА: д = даты["По умолчанию"] // д = 01.01.2001
                                     // новый элемент со значением по умолчанию для типа дата

Для перечисления элементов контейнера и соответствующих им значений индексов применяется цикл ИНДЕКС.

Тип элемента контейнера может быть любым, включая неопределенный тип. Типы индексов могут быть либо стандартными, либо неопределенными. В последнем случае значения индексов могут быть любого типа, но сравнение происходит для их строкового представления.

Примеры

Переменная контейнерного типа с неопределенным типом индекса:

ПЕРЕМ СТРОКА: имена[*]
имена[3.14] = "Пи"
имена[01.01.2015] = "Новый год"
имена[ЦВЕТ[красный=255, зеленый=255]] = "Желтый"

Хотя в предыдущем примере для индекса неопределенного типа указывается значение объектного типа ЦВЕТ, но в определении типа индекса объектный тип не допускается:

ПЕРЕМ СТРОКА: имена_цветов[ЦВЕТ] // ошибка: недопустимый тип индекса

Ограничения на тип индекса связаны с тем, что сравнение на больше/меньше не определено для значений объектных типов, контейнерных типов, интерфейсов и типов функций.

По своей организации индексы делятся на два вида:

Ассоциативные индексы могут быть любого допустимого типа (см. выше). Последовательные индексы всегда имеют целочисленный тип.

Ассоциативный индекс содержит сортированные значения. Поиск значения индекса происходит за время пропорциональное логарифму от количества элементов. Для объявления типа ассоциативного индекса надо просто указать соответствующее имя типа или *.

Пример

// ассоциативный индекс строкового типа
ПЕРЕМ СТРОКА: словарь[СТРОКА]
// ассоциативный индекс неопределенного типа
ПЕРЕМ СТРОКА: имена[*]

Последовательный индекс – это позиция элемента в массиве значений. Доступ к значению элемента происходит за время, которое не зависит от количества элементов. Для последовательного индекса могут быть заданы пределы. Нижний и верхний пределы ограничивают допустимые значения последовательного индекса. Минимальный предел индекса -16777215, а максимальный 16777215. По умолчанию для последовательного индекса применяется диапазон от 1 до 16777215. Выделение памяти для хранения элементов с большими значениями индексов происходит только при создании этих элементов. Следует избегать чрезмерного использования больших массивов элементов, т.к. это может привести к отказу выделения памяти операционной системой. Так для создания элемента с индексом 16 млн. на 32-х битной платформе потребуется выделение 64 Мб непрерывного адресного пространства. При этом не произойдет создания элементов с индексами от 1 до 16 млн. Последовательный индекс позволяет иметь "дырки" в нумерации. Для объявления последовательного индекса после ключевого слова ЦЕЛОЕ в круглых скобках может указываться один или оба предела. Для одномерных последовательных контейнеров с пределами по умолчанию поддерживается упрощенный синтаксис – пустые квадратные скобки.

Примеры

Одномерные последовательные контейнеры (для простоты – произвольный тип элемента):

ПЕРЕМ *: к1[] // упрощенный синтаксис
ПЕРЕМ *: к2[ЦЕЛОЕ()] // тоже самое, что к1, пределы по умолчанию
ПЕРЕМ *: к3[ЦЕЛОЕ(1)] // тоже самое, что к1 и к2, нижний предел указан явно
ПЕРЕМ *: к4[ЦЕЛОЕ(1, 16777215)] // тоже самое, что к1, к2 и к3

ПЕРЕМ *: к5[ЦЕЛОЕ(0)] // пределы от 0 до 16777215
ПЕРЕМ *: к6[ЦЕЛОЕ(-10, 10)] // пределы от -10 до 10

Без круглых скобок – целочисленный ассоциативный индекс:

ПЕРЕМ *: к7[ЦЕЛОЕ] // ассоциативный

В многомерных контейнерах могут произвольно комбинироваться последовательные и ассоциативные индексы. Первый индекс ассоциативный строковый, а второй – последовательный:

ПЕРЕМ *: к8[СТРОКА, ЦЕЛОЕ()]

Матрица вещественных чисел 4 x 4 с индексами от 0 до 3:

ПЕРЕМ ЧИСЛО: матрица[ЦЕЛОЕ(0, 3), ЦЕЛОЕ(0, 3)]

Использование некоторых из объявленных выше переменных контейнерных типов:

к1[0]         = "Альфа"   // ошибка: нижний предел индекса 1
к1[10000000]  = "Бета"    // правильно, но требует много памяти
к2[100]       = "Гамма"   // в к2 только один элемент с индексом 100
к5[0]         = "Дельта"  // все в порядке, нижний предел индекса 0

к7[10000000]  = "Эпсилон" // ассоциативный индекс не требует много памяти

к8["цвет", 1] = "красный" // два разнотипных индекса

матрица[0, 0] = 0.5
матрица[1, 4] = 2.0       // ошибка: второй индекс вне пределов

Контейнерные типы обнаруживаются компилятором при обработке программы автоматически и объявляются неявно. Так две контейнерные переменные с одинаковым типом элемента и описанием индексов будут иметь один тип. У такого неявно объявленного типа не будет имени, доступного для программиста. Чтобы контейнерному типу сопоставить имя можно воспользоваться синонимом типа. При объявлении синонима контейнерного типа используется ключевое слово ИНДЕКС, за которым следует похожее на объявление переменной описание контейнера, где вместо имени переменной указывается символ @.

Примеры

Так можно переписать объявление переменной матрица (см. предыдущий пример) с использованием синонима типа:

ТИП Матрица = ИНДЕКС ЧИСЛО: @[ЦЕЛОЕ(0, 3), ЦЕЛОЕ(0, 3)];

ВЫЧИСЛИТЬ
  ПЕРЕМ ЧИСЛО: матрица1[ЦЕЛОЕ(0, 3), ЦЕЛОЕ(0, 3)] // без использования имени типа
  ПЕРЕМ Матрица: матрица2 // эквивалентное объявление с использованием имени типа
  //...
КОНЕЦ

Синоним контейнерного типа необходим для объявления параметров функции:

ФУНКЦИЯ ОбработкаМатрицы(ПЕРЕМ Матрица: м);

Для значений одинакового контейнерного типа поддерживается копирование и сравнение на равенство и неравенство. Копирование при точном совпадении типа осуществляется быстро – реальное создание копии происходит только при изменении одного из значений. Также поддерживается поэлементное копирование контейнеров разных типов, у которых совместимы типы индексов и элементов.

Пример

ПЕРЕМ ЧИСЛО: к1[СТРОКА]
к1["Альфа"] = 1.4
к1["Бета"]  = 5.8
  
ПЕРЕМ ЧИСЛО: к2[СТРОКА]
к2 = к1 // быстрое копирование
ПЕРЕМ ЛОГИКА: равны1 = к1 = к2 // ДА, тип и эначения равны 

к2["Альфа"] = 2.5 // реальное копирование только здесь, при изменении
ПЕРЕМ ЛОГИКА: равны2 = к1 = к2 // НЕТ, тип тот же, но эначения не равны
  
// переменная с целочисленными элементами - другой тип, но совместимый
ПЕРЕМ ЦЕЛОЕ: к3[СТРОКА] 
к3 = к1 // поэлементное копирование
ОТЛАДКА(к3["Бета"]) // 6, т.к. 5.8 округлилось при поэлементом копировании
ПЕРЕМ ЛОГИКА: равны3 = к1 = к3 // НЕТ, тип отличается

Для объявления именованных констант контейнерного типа используется синтаксис похожий на объявление переменных. В отличие от переменной, обращение к несуществующему элементу индексной константы приведет к ошибке на этапе компиляции или выполнения (в зависимости от того, как задано значение индексов).

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

Значения контейнерного типа могут передаваться в параметрах программы. Необходимым условием для этого является объявление синонима такого типа хотя бы одним модулем используемым программой.


См. также: