Rationale for Ada 2005: 6a Containers
ENG RUS |
4. Sets
@ Sets, like maps, come in two forms: hashed and ordered. Sets are of course just collections of values and there is no question of a key (we can perhaps think of the value as being its own key). Thus in the case of an ordered set the values are stored in order whereas in the case of a map, it is the keys that are stored in order. As well as the usual operations of inserting elements into a set and searching and so on, there are also many operations on sets as a whole that do not apply to the other containers – these are the familiar set operations such as union and intersection.
@ Here is the specification of the ordered sets package giving just those facilities that are common to both kinds of sets.
|
@ The only differences from the maps package (apart from the identifiers) are that there is no key type and both "<" and "=" apply to the element type (whereas in the case of maps, the operation "<" applies to the key type). Thus the ordering relationship "<" defined on elements defines equivalence between the elements whereas "=" defines equality.
@ It is possible for two elements to be equivalent but not equal. For example if they were strings then we might decide that the ordering (and thus equivalence) ignored the case of letters but that equality should take the case into account. (They could also be equal but not equivalent but that is perhaps less likely.) And as in the case of the maps package, the equality operation on elements is only used by the function "=" for comparing two sets.
@ Again we have the usual rules as explained for maps. Thus if x < y is true then y < x must be false; x < y and y < z must imply x < z; and x = y and y = x must be the same.
@ For the convenience of the user the function Equivalent_Elements is declared explicitly. It is equivalent to
|
@ This function Equivalent_Elements corresponds to the formal generic parameter of the same name in the hashed sets package discussed below. This should make it easier to convert between the two forms of packages.
|
@ Note the addition of Equivalent_Sets and To_Set. Two sets are equivalent if they have the same number of elements and the pairs of elements are equivalent. This contrasts with the function "=" where the pairs of elements have to be equal rather than equivalent. Remember that elements might be equivalent but not equal (as in the example of a string mentioned above). The function To_Set takes a single element and creates a set. It is particularly convenient when used in conjunction with operations such as Union described below. The other subprograms are as in the other containers.
|
@ Again these are much as expected except that there is no procedure Update_Element. This is because the elements are arranged in terms of their own value (either by order or through the hash function) and if we just change an element in situ then it might become out of place (this problem does not arise with the other containers). This also means that Replace_Element has to ensure that the value New_Item is not equivalent to an element in a different position; if it is then Program_Error is raised. We will return to the problem of the missing Update_Element later.
|
@ This is just as for the other containers.
|
@ These insert a new element into the set unless an equivalent element already exists. If it does exist then the first one returns with Inserted set to False and Position indicating the element whereas the second raises Constraint_Error (the element value is not changed). If an equivalent element is not in the set then it is added and Position is set accordingly.
|
@ This is somewhat like the last Insert except that if an equivalent element is already in the set then it is replaced (rather than raising Constraint_Error).
|
@ In this case, Constraint_Error is raised if an equivalent element does not already exist.
|
@ If an element equivalent to Item is already in the set, then it is deleted.
|
@ These delete an element. In the first case if there is no such equivalent element then Constraint_Error is raised. In the second case if the cursor is No_Element then again Constraint_Error is also raised – there is also a check to ensure that the cursor otherwise does designate an element in the correct set (remember that cursors designate both an entity and the container); if this check fails then Program_Error is raised.
@ And now some new stuff, the usual set operations.
|
@ These all do exactly what one would expect using the equivalence relation on the elements.
|
@ These are self-evident as well.
|
@ These should be self-evident and are very similar to the corresponding operations on maps. Again unlike the operations on vectors and lists, Find logically searches the whole set and not just starting at some point (there is also no Reverse_Find). Moreover, Find uses the equivalence relation based on the "<" parameter.
|
@ These are also as for other containers.
@ The sets packages conclude with an internal generic package called Generic_Keys. This package enables some set operations to be performed in terms of keys where the key is a function of the element. Note carefully that in the case of a map, the element is defined in terms of the key whereas here the situation is reversed. An equivalence relationship is defined for these keys as well; this is defined by a generic parameter "<" for ordered sets and Equivalent_Keys for hashed sets.
@ In the case of ordered sets the formal parameters are
|
@ The following are then common to the package Generic_Keys for both hashed and ordered sets.
|
@ and then finally
|
@ It is expected that most user of sets will use them in a straightforward manner and that the operations specific to sets such as Union and Intersection will be dominant.
@ However, sets can be used as sort of economy class maps by using the inner package Generic_Keys.
@ Although this is certainly not for the novice we will illustrate how this might be done by reconsidering the stock problem using sets rather than maps. We declare
|
@ Here we have put all the information in the one type.
@ We then declare "<" much as before
|
@ and then instantiate the package thus
|
@ We have used the default generic parameter mechanism for "<" this time by way of illustration.
@ In this case we add items to the store by calling
|
@ The procedure for checking the stock could now become
|
@ This works but is somewhat unsatisfactory. For one thing we have had to make up dummy components in the call of Find (using <>) and moreover we have had to replace the whole of the element although we only wanted to update the Stock component. Moreover, we cannot use Update_Element because it is not defined for sets at all. Remember that this is because it might make things out of order; that wouldn't be a problem in this case because we don't want to change the part number and our ordering is just by the part number.
@ A better approach is to use the part number as a key. We define
|
@ and then
|
@ Note that we do not have to define "<" on the type Part_Key at all because it already exists since Part_Key is an integer type. And the instantiation uses it by default.
@ And now we can rewrite the Request procedure as follows
|
@ This seems hard work but has a number of advantages. The first is that the call of Find is more natural and only involves the part number (the key) – note that this is a call of the function Find in the instantiation of Generic_Keys and takes just the part number. And the other is that the update only involves the component being changed. We mentioned earlier that there was no Update_Element for sets because of the danger of creating a value that was in the wrong place. In the case of the richly named Update_Element_Preserving_Key it also checks to ensure that the element is indeed still in the correct place (by checking that the key is still the same); if it isn't it removes the element and raises Program_Error.
@ But the user is warned to take care when using the package Generic_Keys. It is absolutely vital that the relational operation and the function (Part_No) used to instantiate Generic_Keys are compatible with the ordering used to instantiate the parent package Containers.Ordered_Sets itself. If this is not the case then the sky might fall in.
@ Incidentally, the procedure for checking the stock which previously used the maps package now becomes
|
@ The only change is that the call of Key in
|
@ when using the maps package has been replaced by Element. A minor point is that we could avoid calling Element twice by declaring a constant E in Check_It thus
|
@ and then writing E.Stock < Low and calling Put_Line with E.Part_Number.
@ A more important point is that if we have instantiated the Generic_Keys inner package as illustrated above then we can leave Check_It unchanged to call Key. But it is important to realise that we are then calling the function Key internal to the instantiation of Generic_Keys (flippantly called Party) and not that from the instantiation of the parent ordered sets package (Store_Sets) because that has no such function. This illustrates the close affinity between the sets and maps packages.
@ And finally there is a hashed sets package which has strong similarities to both the ordered sets package and the hashed maps package. We can introduce this much as for hashed maps by giving the differences between the two sets packages, the extra facilities in each and the impact on the part number example.
@ The specification of the hashed sets package starts
|
@ The differences from the ordered sets package are that there is an extra generic parameter Hash and the ordering parameter "<" has been replaced by the function Equivalent_Elements.
@ So if we have
|
@ (which are very similar to the hashed map example – the only changes are to the parameter type name) then we can instantiate the hashed sets package as follows
|
@ and then the rest of our example will be exactly as before. It is thus easy to convert from an ordered set to a hashed set and vice versa provided of course that we only use the facilities common to both.
@ It should also be mentioned that the inner package Generic_Keys for hashed sets has the following formal parameters
|
@ The differences from that for ordered sets are the addition of the function Hash and the replacement of the comparison operator "<" by Equivalent_Keys. (Incidentally the package Generic_Keys for ordered sets also exports a function Equivalent_Keys for uniformity with the hashed sets package.) Although our example itself is unchanged we do have to change the instantiation of Generic_Keys thus
|
@ and then
|
@ The hash function is similar to that used with hashed maps. The type Part_Key and function Part_No are the same as for ordered sets. We don't really need to declare the function Equivalent_Parts since we could use "=" as the actual parameter for Equivalent_Keys.
@ We will finish this discussion of sets by briefly considering the additional facilities in the two sets packages (and their inner generic keys packages) just as we did for the two maps packages (the discussion is almost identical).
@ The ordered sets package has the following additional subprograms
|
@ These are again largely self-evident. The functions Floor and Ceiling are similar to those for ordered maps – Floor searches for the last element which is not greater than Item and Ceiling searches for the first element which is not less than Item – they return No_Element if there is not one.
@ The functions "<" and ">" are very important for ordered sets. The first is equivalent to
|
@ There is a general philosophy that the container packages should work efficiently even if the elements themselves are very large – perhaps even other containers. We should therefore avoid copying elements. (Passing them as parameters is of course no problem since they will be passed by reference if they are large structures.) So in this case the built-in comparison is valuable because it can avoid the copying which would occur if we wrote the function ourselves with the explicit internal calls of the function Element.
@ On the other hand, there is a general expectation that keys will be small and so there is no corresponding problem with copying keys. Thus such built-in functions are less important for maps than sets but they are provided for maps for uniformity.
@ The following are additional in the package Generic_Keys for ordered sets
|
@ This corresponds to the formal generic parameter of the same name in the package Generic_Keys for hashed sets as mentioned earlier.
|
@ These are much as the corresponding functions in the parent package except that they use the formal parameter "<" of Generic_Keys for the search.
@ Hashed sets, like hashed maps also have the facility to specify a capacity as for the vectors package.
@ Thus we have
|
@ The behaviour is much as for vectors and hashed maps. We don't have to set the capacity ourselves since it will be automatically extended as necessary but it might significantly improve performance to do so. Note again that Length (S) cannot exceed Capacity (S) but might be much less.
@ The other additional subprograms for hashed sets are
|
@ Again, these are very important for sets. The first is equivalent to
|
@ and once more we see that the built-in functions can avoid the copying of the type Element that would occur if we wrote the functions ourselves.
Rationale for Ada 2005: 6a Containers
ENG RUS |
4. Наборы
@ Наборы также как и карты могут быть двух видов: хэш и упорядоченными. Наборы - только коллекции значений без какого бы то ни было ключа (мы можем думать о значении как о своего рода ключе самого себя). Таким образом, упорядоченный набор подобен карте где в качестве ключа используется собственное значение элемента. Вместе с обычными операциями вставки элементов в набор, поиска и так далее, есть также много операций на наборах в целом, которые не встречаются в других контейнерах - это свойственные только для наборов операции - объединение и пересечение.
@ Вот спецификация пакета упорядоченных наборов, где представлены только те средства, которые распространены у обоих видов наборов.
|
@ Единственное отличие от пакета карт (кроме идентификаторов) состоит в том, что нет никакого ключевого типа, функции "<" и "=" относятся к типу элемента (тогда как в случае карт, операция "<" относится к типу ключа). Таким образом, отношение упорядочения "<" определённое на элементах определяет эквивалентность между элементами, тогда как "=" определяет равенство.
@ Два элемента могут быть эквивалентными, но не обязательно при этом равными. Например, в случае строк мы могли бы решить, что упорядочение (и, таким образом, эквивалентность) игнорировало бы буквенный регистр, но равенство должно было бы принять во внимание этот факт. (Они могли также быть равными, но не эквивалентными, но это возможно менее вероятно). И как в случае пакета карт, операция равенства на элементах только пользуется функцией "=" для того, чтобы сравнить два набора.
@ Снова у нас есть обычные правила как и для карт. Таким образом, если X < Y является истиной тогда Y < X должно быть ложью; X < Y и Y < Z должен подразумевать X < Z; и X = Y и Y = X должно быть эквивалентно.
@ Для удобства пользователя функция Equivalent_Elements объявлена явно. Она выглядит выполняет примерно следующее:
|
@ Функция Equivalent_Elements соответствует формальному настроечному параметру с таким же именем в пакете хэш - наборов, который будет обсуждаться ниже. Это должно облегчить преобразование между двумя формами пакетов.
|
@ Отметим добавление функций Equivalent_Sets и To_Set. Два набора эквивалентны если у них один и тот же ряд элементов и пары элементов эквивалентны. Это контрастирует с функцией "=" где пары элементов должны быть равными, а не эквивалентными. Помните, что элементы могут быть эквивалентны, но при этом не обязательно должны быть равными (как в примере упомянутой выше строки). Функция To_Set берет единственный элемент и создает набор. Она особенно удобна когда используется вместе с операциями, такими как Union, описывемыми ниже. Другие подпрограммы как в других контейнерах.
|
@ Опять же они такие как и ожидается за исключением того что нет никакой процедуры Update_Element. Это потому что элементы упорядочены в терминах их собственного значения (в соответствии с установленным порядком или через хеш-функцию) и если мы только изменяем элемент на месте тогда это может стать неуместным (эта проблема не возникает с другими контейнерами). Это также означает, что Replace_Element должен гарантировать что значение New_Item не эквивалентно элементу в различной позиции; в этом случае возбуждается исключение Program_Error. Мы вернёмся к проблеме без вести пропавшей Update_Element позже.
|
@ Производит такое же действие как и в других контейнерах.
|
@ Эти процедуры вставляют новый элемент в набор, если эквивалентный элемент ещё не существует. Если он действительно существует тогда первая возвращается с параметром Inserted установленным в False и параметром Position указывающим на элемент, тогда как вторая процедура поднимает исключение Constraint_Error (значение элемента при этом не изменяется). Если эквивалентный элемент не находится в наборе, тогда он добавляется и Position устанавливается соответственно.
|
@ Это несколько походит на последнюю процедуру Insert за исключением того, что если эквивалентный элемент уже находится в наборе тогда, он заменяется (вместо того, чтобы поднять исключение Constraint_Error).
|
@ В этом случае Constraint_Error возбуждается, если эквивалентный элемент не существует.
|
@ Если элемент, эквивалентный Item уже находится в наборе, то он удаляется.
|
@ Они удаляют элемент. В первом случае, если нет такого элемента, возбуждается исключение Constraint_Error. Во втором случае, если курсор - No_Element, также имеет место Constraint_Error - есть также проверка для гарантии того что курсор определяет элемент в правильном наборе (напомним, что курсоры определяют и объект и контейнер); если эта проверка терпит неудачу, возбуждается исключение Program_Error.
@ И теперь некоторый новый материал, обычные операции набора.
|
@ Они все делают точно, что можно было бы ожидать используяь отношение эквивалентности элементов.
|
@ Они также самоочевидны.
|
@ Они должны быть самоочевидными и очень похожи на соответствующие операции карт. Снова в отличие от операций векторов и списков Find логически ищет целый набор, а не только начинающийся в некоторой точке (нет также никакого Reverse_Find). Кроме того, Find использует отношение эквивалентности основанное на параметре "<".
|
@ Они такие же что и для других контейнеров.
@ Пакеты наборов включают внутренний настраиваемый пакет по имени Generic_Keys. Этот пакет позволяет некоторым операциям набора быть выполненными в терминах ключей, где ключ - функция элемента. Обратите внимание, что в случае карты, элемент определен в терминах ключа, тогда как здесь ситуация полностью изменена. Эквивалентные отношения определены для этих ключей также; они определены настраиваемым параметром "<" для упорядоченных наборов и Equivalent_Keys для хэш - наборов.
@ В случае упорядоченного набора формальные параметры следующие:
|
@ Следующее является общим для пакета Generic_Keys как для хэш так и для упорядоченных наборов.
|
@ и в конце мы имеем:
|
@ Ожидается, что большинство пользователей наборов будет использовать их прямым способом и что операции, определенные для наборов такие как Union и Intersection будут доминирующими.
@ Однако, наборы могут использоваться как вид карт экономического класса при использовании внутреннего пакета Generic_Keys.
@ Хотя это конечно не для новичка, мы покажем пример того как это можно использовать наборы вместо карт. Мы объявляем:
|
@ Здесь мы поместили всю информацию в один тип.
@ Мы тогда объявляем "<" как обычно:
|
@ и затем иллюстрируем пакет таким образом:
|
@ Мы использовали механизм заданный по умолчанию для настраиваемого параметра "<" на сей раз посредством иллюстрации.
@ В этом случае мы добавляем элементы к памяти следующим образом:
|
@ Процедура для проверки стойки может теперь быть такой:
|
@ Хотя это и работает, но не совсем удовлетворительно. С одной стороны, мы должны были вставить фиктивные компоненты в вызове Find (используя <>), кроме того, мы должны были заменить весь элемент, хотя мы только хотели обновить Stock компонент. Кроме того, мы не можем использовать Update_Element, потому что он не определен для наборов вообще. Напомним, что это могло бы нарушить порядок элементов; но в данном случае это не было бы проблемой, потому что мы не хотим менять числовой код запчасти.
@ Лучший подход должен использовать числовой код запчасти как ключ. Мы определяем:
|
@ и тогда:
|
@ Отметим, что мы не должны определять функцию "<" для типа Part_Key вообще, потому что она уже существует, так как Part_Key - целочисленный тип. И реализация использует соответствующую функцию по умолчанию.
@ И теперь мы можем перезаписать процедуру Request следующим образом:
|
@ Это кажется тяжелой работой, но имеет ряд преимуществ. Во-первых, вызов Find является более естественным и вовлекает только числовой код запчасти (ключ) - отметим, что это вызов функции Find в реализации Generic_Keys использует только числовой код запчасти. Во-вторых, это обновление вовлекает только изменяемый компонент. Мы упоминали ранее, что нет никакого Update_Element для наборов из-за опасности создания значения, которое было бы в неправильном месте. В случае длинно названного Update_Element_Preserving_Key также выполняется проверка для гарантии того что элемент находится действительно все еще в правильном месте (проверяется что ключ - всё ещё тот же самый); если это не так, то элемент удаляется и возбуждается исключение Program_Error.
@ Если пользователь пожелает воспользоваться пакетом Generic_Keys абсолютно жизненно важно, чтобы относительная операция и функция (Part_No), используемая для иллюстрации Generic_Keys, были совместимы с упорядочением, используемым для иллюстрации родительского пакета Containers.Ordered_Sets непосредственно. Если дело обстоит не так, тогда небо могло бы обрушиться.
@ Случайно, процедура для того, чтобы проверить стойку, которая ранее использовала пакет карт теперь становится:
|
@ Здесь единственное изменение состоит в том, что вызов Key в:
|
@ заменен на Element. Небольшое отличие состоит в том, что мы можем избежать вызова Element дважды, объявляя константу E в Check_It таким образом:
|
@ и затем написав E.Stock < Low и вызывая Put_Line с E.Part_Number.
@ Более важный момент состоит в том, что если мы проиллюстрировали внутренний пакет Generic_Keys как было проиллюстрировано выше, тогда мы можем оставить Check_It неизменной, чтобы вызвать Key. Но важно понять, что тогда мы вызываем функцию Key внутренней к реализации Generic_Keys (flippantly вызываемый абонент) а не от реализации родительского пакета упорядоченных наборов (Store_Sets), потому что у этого нет такой функции. Это показывает тесную близость между пакетами наборов и пакетами карт.
@ И наконец, есть пакет хэш наборов, у которого есть сильные общие черты c пакетом упорядоченных наборов и пакетом хэш - карт. Мы можем ввести его как хэш карту, давая различия с пакетом наборов, дополнительными средствами в каждом и воздействии на примере обработки числового кода запчасти.
@ Спецификация пакета хэш - наборов начинается:
|
@ Отличие от пакета упорядоченных наборов состоит в том, что появляется дополнительный настроечный параметр Hash, а параметр упорядочения "<" заменяется функцией Equivalent_Elements.
@ Так, если мы имеем:
|
@ (которые очень похожи на пример хэш - карты, за исключением имени типа параметра), тогда мы можем иллюстрировать пакет хэш - наборов следующим образом:
|
@ и затем остальная часть нашего примера будет как и прежде. Таким образом, это легко преобразовать из упорядоченного набора в хэш - набор и наоборот, конечно, это возможно только если мы используем средства обычные для их обоих.
@ Нужно также упомянуть, что у внутреннего пакета Generic_Keys для хэш - наборов есть следующие формальные параметры:
|
@ Отличие от упорядоченных наборов состоит в добавлении функции Hash и замене оператора сравнения "<" на функцию Equivalent_Keys. (Кстати, пакет Generic_Keys для упорядоченных наборов также экспортирует функцию Equivalent_Keys для однородности с пакетом хэш - наборов). Хотя сам наш пример неизменен, мы действительно должны изменить реализацию Generic_Keys таким образом:
|
@ и тогда:
|
@ Хеш-функция подобна используемой с хэш - картами. Тип Part_Key и функция Part_No такая же что и у упорядоченных наборов. Мы действительно не должны объявлять функцию Equivalent_Parts, так как мы можем использовать "=" как фактический параметр для Equivalent_Keys.
@ Мы закончим обсуждение наборов кратко рассматривая дополнительные средства в двух пакетах наборов (и их внутренние настраиваемые пакеты ключей) так же как мы это делали для двух пакетов карт (обсуждение почти идентично).
@ У пакета упорядоченных наборов есть следующие дополнительные подпрограммы:
|
@ Они снова в значительной степени самоочевидны. Функции Floor и Ceiling такие же что и для упорядоченных карт - Floor ищет последний элемент, который не больше чем Item, а Ceiling ищет первый элемент, который не меньше чем Item - все они возвращают No_Element, если такого элемента нет.
@ Функции "<" и ">" очень важны для упорядоченных наборов. Первая эквивалентна:
|
@ Есть общая философия, что контейнерные пакеты должны работать эффективно, даже если сами элементы являются очень большими - возможно даже другими контейнерами. Мы должны поэтому избегать копирования элементов. (Передача их как параметры не является никакой проблемой, так как они передаются по ссылке, если они окажутся большими структурами). Таким образом, в этом случае, встроенное сравнение ценно, потому что оно позволяет избегать копирования, которое произошло бы, если бы мы написали функцию самостоятельно с явными внутренними запросами функции Element.
@ С другой стороны, есть общее ожидание, что ключи будут маленькими и, таким образом, нет никакой соответствующей проблемы с копированием ключей. Таким образом, такие встроенные функции менее важны для карт чем для наборов, но их предоставляют для карт для однородности.
@ Следующее дополнение в пакете Generic_Keys для упорядоченных наборов:
|
@ Это соответствует формальному настроечному параметру с токим же именем в пакете Generic_Keys для хэш - наборов упомянутых ранее.
|
@ Они эквивалентны аналогичным функциям родительского пакета за исключением того, что они используют формальный параметр "<" для Generic_Keys для поиска.
@ У хэш - наборов как и хэш - карт имеется средство для определения ёмкости как и у векторного пакета.
@ Таким образом мы имеем:
|
@ Поведение такое же как для векторов и хэш - карт. Мы не обязаны устанавливать ёмкость самостоятельно, так как она будет автоматически расширена по мере необходимости, но мы можем значительно улучшить производительность если мы установим ёмкость заранее. Обратите внимание, что Length (S) не может превысить Capacity (S).
@ Другие дополнительные подпрограммы для хэш - наборов:
|
@ Снова, они очень важны для наборов. Первая выполняет примерно следующее:
|
@ и еще раз мы видим, что встроенные функции могут избежать копирования типа Element, которое произошло бы, если бы мы написали функции самостоятельно.
2010-10-24 00:26:58
медицинские справки в гаи новая статья . . .