Rationale for Ada 2005: 6a Containers
ENG RUS |
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
|
@ where we see that the formal type Element_Type has unknown discriminants and so permits the actual type to be any indefinite type (and indeed a definite type as well). So if we want to manipulate lists of strings where the individual strings can be of any length then we declare package String_Lists is new Ada.Containers.Indefinite_Doubly_Linked_Lists (String); In the case of ordered maps we have
|
@ showing that both Element_Type and Key_Type can be indefinite.
@ There are two other differences from the definite versions which should be noted.
@ One is that the Insert procedures for Vectors, Lists and Maps which insert an element with its default value are omitted (because there is no way to create a default initialized object of an indefinite type anyway).
@ The other is that the parameter Element of the access procedure Process of Update_Element (or the garrulous Update_Element_Preserving_Key in the case of sets) can be constrained even if the type Element_Type is unconstrained.
@ As an example of the use of an indefinite container consider the problem of creating an index. For each word in a text file we need a list of its occurrences. The individual words can be represented as just objects of the type String. It is perhaps convenient to consider strings to be the same irrespective of the case of characters and so we define
|
@ where the function To_Lower is from the package Ada.Characters.Handling.
@ We can suppose that the positions of the words are described by a type Place thus
|
@ The index is essentially a map from the type String to a list of values of type Place. We first create a definite list container for handling the lists thus package Places is new Doubly_Linked_Lists (Place); We then create an indefinite map container from the type String to the type List thus
|
@ The index is then declared by writing
|
@ Note that this example illustrates the use of nested containers since the elements in the map are themselves containers (lists).
@ It might be helpful for the index to contain information saying which file it refers to. We can extend the type Map thus (remember that container types are tagged)
|
@ and now we can more usefully declare
|
@ We can now declare various subprograms to manipulate our map. For example to add a new item we have first to see whether the word is already in the index – if it is not then we add the new word to the map and set its list to a single element whereas if it is already in the index then we add the new place entry to the corresponding list. Thus
|
@ A number of points should be observed. The type Text_Map being derived from Indexes.Map inherits all the map operations and so we can write Index.Find (Word) which uses the prefixed notation (or we can write Indexes.Find (Index, Word)). On the other hand auxiliary entities such as the type Cursor and the constant No_Element are of course in the package Indexes and have to be referred to as Indexes.Cursor and so on.
@ A big problem with the procedure as written however is that it uses Element and Replace_Element rather than Update_Element. This means that it copies the whole of the existing list, adds the new item to it, and then copies it back. Here is an alternative version
|
@ This is still somewhat untidy. In the case of a new word we might as well make the new map entry with an empty list and then update it thereby sharing the calls of Append. We get
|
@ It will be recalled that there are various versions of Insert. We have used that which has two out parameters being the position where the node was inserted and a Boolean parameter indicating whether a new node was inserted or not. In this case we know that it will be inserted and so the final parameter is a nuisance (but sadly we cannot default out parameters). Note also that we need not give the parameter Places.Empty_List because another version of Insert will do that automatically since that is the default value of a list anyway.
@ Yet another approach is not to use Find but just call Insert. We can even use the defaulted version – if the word is present then the node is not changed and the position parameter indicates where it is, if the word is not present then a new node is made with an empty list and again the position parameter indicates where it is.
|
@ Curiously enough we do not need to use the value of Inserted. We leave the reader to decide which of the various approaches is best.
@ We can now do some queries on the index. For example we might want to know how many different four-lettered words there are in the text. We can either use Iterate or do it ourselves with Next as follows
|
@ We might finally wish to know how many four-lettered words there are on a particular page. (This is just an exercise – it would clearly be simplest to search the original text!) We use Iterate this time both to scan the map for the words and then to scan each list for the page number
|
@ We could of course have used First and Next to search the list. But in any event the important point is that by using Query_Element we do not have to copy the list in order to scan it.
Rationale for Ada 2005: 6a Containers
ENG RUS |
5. Неопределённые Контейнеры
@ Для шести контейнерных пакетов, которые мы только что обсуждали имеются версии и для неопределенных типов.
@ Как было упомянуто в Разделе 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
Диван Рио . форекс. . .