Rationale for Ada 2005: 6a Containers
RUSTOPBACKNEXT
ENG |
5. Indefinite containers
@ There are versions of the six container packages we have just been discussing for indefinite types. @ As mentioned in Section 1, an indefinite (sub)type is one for which we cannot declare an object without giving a constraint (either explicitly or though an initial value). Moreover we cannot have an array of an indefinite subtype. The type String is a good example. Thus we cannot declare an array of the type String because the components might not all be the same size and indexing would be a pain. Class wide types are also indefinite. @ The specification of the indefinite container for lists starts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale for Ada 2005: 6a Containers
ENGRUSTOPBACKNEXT5. Неопределённые Контейнеры
@ Для шести контейнерных пакетов, которые мы только что обсуждали имеются версии и для неопределенных типов.
@ Как было упомянуто в Разделе 1, неопределенный тип (подтип) это тот для которого мы не можем объявить объект не задав какого-нибудь ограничения (или явно или через задание начального значения). Кроме того, у нас не может быть массивов из элементов неопределенного подтипа. Тип String тому хороший пример. Т.е., мы не можем объявить массив элементов типа String, потому что строки могут быть разных размеров и индексация в этом случае весьма проблематична. Класс широких типов также является неопределённым.
@ Спецификация неопределенного контейнера для списков начинается так:
|
@ где мы видим, что формальный тип Element_Type имеет неизвестные дискриминанты и, таким образом, это разрешает фактическому типу быть любым неопределенным типом (и действительно определенным типом также). Так, если мы хотим управлять списками строк, где индивидуальные строки могут иметь разную длину тогда, мы объявляем package String_Lists is new Ada.Containers.Indefinite_Doubly_Linked_Lists (String); В случае упорядоченных карт мы имеем:
|
@ показывая что и Element_Type и Key_Type могут быть неопределенными.
@ Есть два других отличия от ранее определенных версий, которые стоит упомянуть.
@ Первое - это исключение процедур Insert для Векторов, Списков и Карт, которые вставляет элемент с его значением по умолчанию (потому что нет никакого способа создать значение по умолчанию для объекта неопределенного типа так или иначе).
@ Второе это то, что параметр Element в ссылке на процедуру Process в процедуре Update_Element (или её многословный вариант Update_Element_Preserving_Key в случае наборов) может быть сдержан, даже если тип Element_Type беспрепятственный.
@ Как пример использования неопределенного контейнера рассмотрим проблему создания индекса. Для каждого слова в текстовом файле мы можем пожелать иметь список вхождений. Индивидуальные слова могут быть представлены только как объекты типа String. Возможно удобно полагать, что значение строки не чувствительно к регистру символов, и таким образом, мы определяем:
|
@ где функция To_Lower из пакета Ada.Characters.Handling.
@ Мы можем предположить, что позиции слов описаны типом Place таким образом:
|
@ Индекс - это по существу карта от типа String к списку значений типа Place. Мы сначала создаем определенный списковый контейнер для обработки списков: package Places is new Doubly_Linked_Lists (Place); Затем мы создаем неопределенный контейнер карты от типа String типу List:
|
@ Индекс тогда объявляем следующим образом:
|
@ Отметим, что этот пример иллюстрирует использование вложенных контейнеров, так как элементы в карте - сами по себе являются контейнерами (списками).
@ Было бы полезно для индекса содержать информацию, говорящую, к какому файлу он обращается. Мы можем расширить тип Map таким образом (напомним, что контейнерные типы являются тэговыми).
|
@ и теперь мы можем более естественно объявить
|
@ Теперь мы можем объявить различные подпрограммы для управления нашей картой. Например, чтобы добавить новый элемент мы должны сначала убедиться, является ли уже такое слово в индексе - если это не так тогда, мы добавляем новое слово к карте и устанавливаем ее список в единственный элемент, если же такое слово уже находится в индексе тогда, мы добавляем новую точку входа в соответствующий список таким образом:
|
@ Обратим внимание на некоторые моменты. Так тип Text_Map, получаемый из Indexes.Map наследует все операции карты и, таким образом, мы можем написать Index.Find (Word) c использованием префиксной нотации (или мы можем написать Index.Find (Index, Word)). С другой стороны, вспомогательные объекты, такие как тип Cursor и константа No_Element находятся непосредственно в пакете Indexes и должны упоминаться как Indexes.Cursor и так далее.
@ Используя процедуры Element и Replace_Element, а не процедуру Update_Element фактически мы сначала копируем весь существующий список, добавляем новый элемент, а затем копируем весь его обратно. Рассмотрим альтернативный вариант:
|
@ Это всё ещё несколько неопрятно. В случае нового слова мы могли бы также сделать новый вход карты с пустым списком а затем обновить его используя вызовы Append:
|
@ Здесь будет выбрана одна из нескольких возможных версий процедуры Insert. Мы использовали ту, которая имеет два параметра, первый из которых указывает позицию, где узел был вставлен, а второй типа Boolean, указывающий, был ли новый узел вставлен или нет. В этом случае мы знаем, что элемент будет вставлен и, таким образом, последний параметр будет неприятностью (печально, но мы не можем использовать значение по умолчанию для параметров типа out). Отметим также, что мы не должны задавать параметр Places.Empty_List, потому что другая версия процедуры Insert сделает это автоматически, так как это - значение по умолчанию списка так или иначе.
@ Еще один подход, мы можем не использовать процедуру Find. Вместо неё мы можем использовать не всегда выполняющую свои обязательства версию процедуры Insert - если слово присутствует тогда, узел не меняется, а параметр позиции указывает, где он находится, если слово не присутствует тогда, создаётся новый узел с пустым списком, и снова параметр позиции указывает, где это произошло.
|
@ Весьма любопытно, мы не должны использовать значение Inserted. Мы оставляем читателя, чтобы решить, какой из различных подходов лучше.
@ Теперь мы можем сделать несколько запросов к индексу. Например, мы могли бы хотеть знать сколько различных непристойных слов находятся в тексте. Мы можем или использовать процедуру Iterate или сделать это непосредственно используя процедуру Next следующим образом:
|
@ Мы могли бы наконец пожелать узнать сколько непристойных слов находятся на определённой странице. (Это - только для иллюстрации - ясно что искать это в оригинальном тексте горахдо проще!) Мы используем Iterate на сей раз чтобы просмотреть карту для слов а затем просмотреть каждый список для номера страницы.
|
@ Мы, конечно, могли использовать процедуры First и Next для поиска в списке. Но в любом случае важный момент состоит в том, что при использовании Query_Element мы не копируем список, чтобы просмотреть его.
2010-10-24 00:26:58
. .