4.3.5 Container Aggregates
Glossary entry: A construct used to define
a value of a type that represents a collection of elements, by explicitly
specifying the elements in the collection.
{
AI12-0212-1}
For a type other than an array type, the following type-related operational
aspect may be specified:
Aggregate
This aspect is an
aggregate
of the form:
Aspect Description for Aggregate:
Mechanism to define user-defined aggregates.
(Empty =>
name[,
Add_Named =>
procedure_name][,
Add_Unnamed =>
procedure_name][,
New_Indexed =>
function_name,
Assign_Indexed =>
procedure_name])
{
AI12-0212-1}
The type for which this aspect is specified is known as the
container
type of the Aggregate aspect.
A
procedure_name
shall be specified for at least one of Add_Named, Add_Unnamed, or Assign_Indexed.
If Add_Named is specified, neither Add_Unnamed nor Assign_Indexed shall
be specified. Either both or neither of New_Indexed and Assign_Indexed
shall be specified.
Name Resolution Rules
{
AI12-0212-1}
The
name specified
for Empty for an Aggregate aspect shall denote a constant of the container
type, or denote a function with a result type of the container type that
has no parameters, or that has one
in parameter of type Integer.
Reason: In the function case, the parameter,
if present may be used to specify an initial size for the container,
in anticipation of adding elements to it. For a positional aggregate,
or a named aggregate that doesn't use an iterator, it will be initialized
with the number of elements. For a named aggregate that uses an iterator,
the implementation is permitted to estimate the number of elements that
the iterator will produce, but it is not required to do so.
{
AI12-0212-1}
The
procedure_name
specified for Add_Unnamed for an Aggregate aspect shall denote a procedure
that has two parameters, the first an
in out parameter of the
container type, and the second an
in parameter of some nonlimited
type, called the
element type of the container type.
{
AI12-0212-1}
The
function_name
specified for New_Indexed for an Aggregate aspect shall denote a function
with a result type of the container type, and two parameters of the same
discrete type, with that type being the
key type of the container
type.
Reason: The New_Indexed function is used
instead of Empty as the first step of creating an aggregate that is initialized
using the Assign_Indexed procedure.
{
AI12-0212-1}
The
procedure_name
specified for Add_Named or Assign_Indexed for an Aggregate aspect shall
denote a procedure that has three parameters, the first an
in out
parameter of the container type, the second an
in parameter of
a nonlimited type (the
key type of the container type), and the
third, an
in parameter of a nonlimited type that is called the
element type of the container type.
Legality Rules
{
AI12-0212-1}
If the container type of an Aggregate aspect is a private type, the full
type of the container type shall not be an array type. If the container
type is limited, the name specified for Empty shall denote a function
rather than a constant object.
{
AI12-0212-1}
For an Aggregate aspect, the key type of Assign_Indexed shall be the
same type as that of the parameters of New_Indexed. Additionally, if
both Add_Unnamed and Assign_Indexed are specified, the final parameters
shall be of the same type — the element type of the container type.
Static Semantics
Syntax
Reason: {
AI12-0212-1}
Unlike other
aggregates,
container aggregates have to be surrounded with brackets rather than
parentheses. Using brackets allows writing zero- and one-element positional
aggregates.
(Were we to use parentheses, a one-element positional aggregate would
look the same as and would always be interpreted as a parenthesized expression.)
Unlike the case for arrays, where it is always possible to give bounds
to work around this gap, such an
aggregate
could not be written at all for a sequence type (such as a list) where
there is no index or key to use.
Name Resolution Rules
Legality Rules
Reason: Only an element of an indexed
aggregate can be left uninitialized, because the compiler can simply
omit producing an Assign_Indexed operation for the given index. For other
kinds of aggregates, there are no operations that add an element without
giving it a value. We could have defined such (optional) operations,
but they would have added significant complication to an already complex
feature without adding much additional expressive power. Note that, to
be consistent with array aggregates, we do not support specifying <>
in an
iterated_element_association.
Ramification: If there is a
key_expression
in an
iterated_element_association,
it determines the key of each added key/value pair, rather than the loop
parameter. But if there is no
key_expression,
the loop parameter itself is used as the key.
Dynamic Semantics
if the
aggregate
is an indexed aggregate, from the result of a call on the New_Indexed
function; the actual parameters in this call represent the lower and
upper bound of the
aggregate,
and are determined as follows:
if the
aggregate
is a
positional_container_aggregate,
the lower bound is the low bound of the subtype of the key parameter
of the Add_Indexed procedure, and the upper bound has a position number
that is the sum of the position number of the lower bound and one less
than the number of
expressions
in the
aggregate;
if the
aggregate
is not an indexed aggregate, by assignment from the Empty constant, or
from a call on the Empty function specified in the Aggregate aspect.
In the case of an Empty function with a formal parameter, the actual
parameter has the following value:
otherwise, to an implementation-defined
value.
Implementation Note: This value ought
to be an estimate for the number of elements in the
aggregate,
if one is available. If not, it is suggested to use the default value
for the parameter if one exists, and zero otherwise.
for a
positional_container_aggregate
of a type with a specified Add_Unnamed procedure, each
expression
is evaluated in an arbitrary order,
and the Add_Unnamed
procedure is invoked in sequence with the anonymous object
A as
the first parameter and the result of evaluating each
expression
as the second parameter, in the order of the
expressions;
for a
positional_container_aggregate
that is an indexed aggregate, each
expression
is evaluated in an arbitrary order,
and the Assign_Indexed
procedure is invoked in sequence with the anonymous object
A as
the first parameter, the key value as the second parameter, computed
by starting with the low bound of the subtype of the key formal parameter
of the Assign_Indexed procedure and taking the successor of this value
for each successive
expression,
and the result of evaluating each
expression
as the third parameter;
otherwise, with the loop parameter as
the second parameter;
for a
named_container_aggregate
that is an indexed aggregate, the evaluation proceeds as above for the
case of Add_Named, but with the Assign_Indexed procedure being invoked
in its stead; in the case of a
container_element_association
with a <> rather than an
expression,
the corresponding call on Assign_Indexed is not performed, leaving the
component as it was upon return from the New_Indexed function;
1.
2.
{
AI12-0212-1}
{
AI12-0327-1}
an iteration is performed, and for each value conditionally produced
by the iteration (see
5.5 and
5.5.2)
the Add_Unnamed procedure is invoked, with the anonymous object
A
as the first parameter and the result of evaluating the
expression
as the second parameter.
Examples
{
AI12-0212-1}
Declarations of Set_Type, Map_Type, and Vector_Type:
-- Set_Type is a set-like container type.
type Set_Type is private
with Aggregate => (Empty => Empty_Set,
Add_Unnamed => Include);
function Empty_Set return Set_Type;
subtype Small_Natural is Natural range 0..1000;
procedure Include (S : in out Set_Type; N : in Small_Natural);
-- Map_Type is a map-like container type.
type Map_Type is private
with Aggregate => (Empty => Empty_Map,
Add_Named => Add_To_Map);
procedure Add_To_Map (M : in out Map_Type; Key : in Integer; Value : in String);
Empty_Map : constant Map_Type;
-- Vector_Type is an extensible array-like container type.
type Vector_Type is private
with Aggregate => (Empty => Empty_Vector,
Add_Unnamed => Append_One,
New_Indexed => New_Vector,
Assign_Indexed => Assign_Element);
function Empty_Vector (Capacity : Count_Type := 0) return Vector_Type;
procedure Append_One (V : in out Vector_Type; New_Item : in String);
procedure Assign_Element (V : in out Vector_Type;
Index : in Positive;
Item : in String);
function New_Vector (First, Last : Positive) return Vector_Type
with Pre => First = Positive'First;
-- Vectors are always indexed starting at the
-- lower bound of their index subtype.
-- Private part not shown.
{
AI12-0212-1}
Examples of container aggregates for Set_Type, Map_Type, and Vector_Type:
-- Example aggregates using Set_Type.
S : Set_Type;
-- Assign the empty set to S:
S := [];
-- Is equivalent to:
S := Empty_Set;
-- A positional set aggregate:
S := [1, 2];
-- Is equivalent to:
S := Empty_Set;
Include (S, 1);
Include (S, 2);
-- Is equivalent to:
S := Empty_Set;
for Item in 1 .. 5 loop
Include (S, Item * 2);
end loop;
{
AI12-0379-1}
--
Is equivalent (assuming set semantics) to:
S := Empty_Set;
for Item
in -5 .. 5
loop
if Item /= 0
then
Include (S, Item);
end if;
end loop;
-- Example aggregates using Map_Type.
M : Map_Type;
-- A simple named map aggregate:
M := [12 => "house", 14 => "beige"];
-- Is equivalent to:
M := Empty_Map;
Add_To_Map (M, 12, "house");
Add_To_Map (M, 14, "beige");
-- Define a table of pairs:
type Pair is record
Key : Integer;
Value : access constant String;
end record;
Table : constant array(Positive range <>) of Pair :=
[(Key => 33, Value => new String'("a nice string")),
(Key => 44, Value => new String'("an even better string"))];
-- Is equivalent to:
M := Empty_Map;
for P of Table loop
Add_To_Map (M, P.Key, P.Value.all);
end loop;
-- Create an image table for an array of integers:
Keys : constant array(Positive range <>) of Integer := [2, 3, 5, 7, 11];
--
A map aggregate where the values produced by the
--
iterated_element_association are of the same type as the key
--
(eliminating the need for a separate key_expression):
M := [
for Key
of Keys => Integer'Image (Key)];
-- Is equivalent to:
M := Empty_Map;
for Key of Keys loop
Add_To_Map (M, Key, Integer'Image (Key));
end loop;
-- Example aggregates using Vector_Type.
V : Vector_Type;
-- A positional vector aggregate:
V := ["abc", "def"];
-- Is equivalent to:
V := Empty_Vector (2);
Append_One (V, "abc");
Append_One (V, "def");
-- An indexed vector aggregate:
V := [1 => "this", 2 => "is", 3 => "a", 4 => "test"];
-- Is equivalent to:
V := New_Vector (1, 4);
Assign_Element (V, 1, "this");
Assign_Element (V, 2, "is");
Assign_Element (V, 3, "a");
Assign_Element (V, 4, "test");
-- A vector made from the elements of a map:
V := [for Elem of M => Elem];
-- Is equivalent to:
V := Empty_Vector (<estimate of size of M>);
for Elem of M loop
Add_Positional (V, Elem);
end loop;
Extensions to Ada 2012
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe