5.5. Loop Statements
This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue
[A loop_statement
includes a sequence_of_statements
that is to be executed repeatedly, zero or more times with the iterations running sequentially or concurrently with one another.]
Syntax
2loop_statement
::=
[loop_statement_identifier
:]
[iteration_scheme
] loop
sequence_of_statements
end loop [loop_identifier
];
3/5
iteration_scheme
::=
while condition
| for loop_parameter_specification
| for iterator_specification
| [parallel [aspect_specification
]]
for procedural_iterator
| parallel [(chunk_specification
)] [aspect_specification
]
for loop_parameter_specification
| parallel [(chunk_specification
)] [aspect_specification
]
for iterator_specification
3.1/5
chunk_specification
::=
integer_simple_expression
| defining_identifier
in discrete_subtype_definition
4/5
loop_parameter_specification
::=
defining_identifier
in [reverse] discrete_subtype_definition
[iterator_filter
]
4.1/5
iterator_filter
::=
whencondition
5
If a loop_statement
has a loop_statement_identifier
, then the identifier
shall be repeated after the end loop; otherwise, there shall not be an identifier
after the end loop.
An iteration_scheme
that begins with the reserved word parallel shall not have the reserved word reverse in its loop_parameter_specification
.
Name Resolution Rules
5.2/5In a chunk_specification
that is an integer_simple_expression
, the integer_simple_expression
is expected to be of any integer type.
Static Semantics
6/5A loop_parameter_specification
declares a loop parameter, which is an object whose subtype (and nominal subtype) is that defined by the discrete_subtype_definition
.
In a chunk_specification
that has a discrete_subtype_definition
, the chunk_specification
declares a chunk parameter object whose subtype (and nominal subtype) is that defined by the discrete_subtype_definition
.
Dynamic Semantics
6.2/5The filter of an iterator construct (a loop_parameter_specification
, iterator_specification
, or procedural_iterator
) is defined to be satisfied when there is no iterator_filter
for the iterator construct, or when the condition
of the iterator_filter
evaluates to True for a given iteration of the iterator construct.
Term entry: iterator filter — construct that is used to restrict the elements produced by an iteration to those for which a boolean condition evaluates to True
If a sequence_of_statements
of a loop_statement
with an iterator construct is said to be conditionally executed, then the statement
s are executed only when the filter of the iterator construct is satisfied.
The loop iterators loop_parameter_specification
and iterator_specification
can also be used in contexts other than loop_statement
s (for example, see 4.3.5 and 4.5.8). In such a context, the iterator conditionally produces values in the order specified for the associated construct below or in 5.5.2. The values produced are the values given to the loop parameter when the filter of the iterator construct is satisfied for that value. [No value is produced when the condition
of an iterator_filter
evaluates to False.]
For the execution of a loop_statement
, the sequence_of_statements
is executed zero or more times, until the loop_statement
is complete. The loop_statement
is complete when a transfer of control occurs that transfers control out of the loop, or, in the case of an iteration_scheme
, as specified below.
For the execution of a loop_statement
with a while iteration_scheme
, the condition is evaluated before each execution of the sequence_of_statements
; if the value of the condition
is True, the sequence_of_statements
is executed; if False, the execution of the loop_statement
is complete.
If the reserved word parallel is present in the iteration_scheme
of a loop_statement
(a parallel loop), the iterations are partitioned into one or more chunks, each with its own separate logical thread of control (see Clause 9). If a chunk_specification
is present in a parallel loop, it is elaborated first, and the result of the elaboration determines the maximum number of chunks used for the parallel loop. If the chunk_specification
is an integer_simple_expression
, the elaboration evaluates the expression, and the value of the expression determines the maximum number of chunks. If a discrete_subtype_definition
is present, the elaboration elaborates the discrete_subtype_definition
, which defines the subtype of the chunk parameter, and the number of values in this subtype determines the maximum number of chunks. After elaborating the chunk_specification
, a check is made that the determined maximum number of chunks is greater than zero. If this check fails, Program_Error is raised.
For the execution of a loop_statement
that has an iteration_scheme
including a loop_parameter_specification
, after elaborating the chunk_specification
and aspect_specification
, if any, the loop_parameter_specification
is elaborated. This elaborates the discrete_subtype_definition
, which defines the subtype of the loop parameter. If the discrete_subtype_definition
defines a subtype with a null range, the execution of the loop_statement
is complete. Otherwise, the sequence_of_statements
is conditionally executed once for each value of the discrete subtype defined by the discrete_subtype_definition
that satisfies the predicates of the subtype (or until the loop is left as a consequence of a transfer of control). Prior to each such iteration, the corresponding value of the discrete subtype is assigned to the loop parameter associated with the given iteration. If the loop is a parallel loop, each chunk has its own logical thread of control with its own copy of the loop parameter; otherwise (a sequential loop), a single logical thread of control performs the loop, and there is a single copy of the loop parameter. Each logical thread of control handles a distinct subrange of the values of the subtype of the loop parameter such that all values are covered with no overlaps. Within each logical thread of control, the values are assigned to the loop parameter in increasing order unless the reserved word reverse is present, in which case the values are assigned in decreasing order. In the absence of a transfer of control, the associated parallel construct of a loop_parameter_specification
is complete when all of its logical threads of control are complete.
To be honest: This wording does not describe when the loop parameter object(s) are created. That creation has no side-effects (other than possibly raising Storage_Error, but anything can do that), so we simplified the wording by leaving it out. Each object has to be created before any iteration that depends on it starts, but we do not (for instance) require that the objects are all created at once at the start of the loop, nor that the objects are created after the elaboration of the discrete_subtype_definition
.
The predicate (if any) necessarily has to be a static predicate as a dynamic predicate is explicitly disallowed — see 3.2.4.
If there is a predicate, the loop still visits the values in the order of the underlying base type; the order of the values in the predicate is irrelevant. This is the case so that the following loops have the same sequence of calls and parameters on procedure Call for any subtype S:
for I in S loop
Call (I);
end loop;
9.e
for I in S'Base loop
if I in S then
Call (I);
end if;
end loop;
The rules for completing a parallel construct when there is a transfer of control are given in 5.1.
If a chunk_specification
with a discrete_subtype_definition
is present, then the logical thread of control associated with a given chunk has its own copy of the chunk parameter initialized with a distinct value from the discrete subtype defined by the discrete_subtype_definition
. The values of the chunk parameters are assigned such that they increase with increasing values of the ranges covered by the corresponding loop parameters.
Whether or not a chunk_specification
is present in a parallel loop, the total number of iterations of the loop represents an upper bound on the number of logical threads of control devoted to the loop.
[For details about the execution of a loop_statement
with the iteration_scheme
including an iterator_specification
, see 5.5.2. For details relating to a procedural_iterator
, see 5.5.3.]
NOTE 1 A loop parameter declared by a loop_parameter_specification
is a constant; it cannot be updated within the sequence_of_statements
of the loop (see 3.3).
NOTE 2 No separate object_declaration
is expected for a loop parameter, since the loop parameter is automatically declared by the loop_parameter_specification
. The scope of a loop parameter extends from the loop_parameter_specification
to the end of the loop_statement
, and the visibility rules are such that a loop parameter is only visible within the sequence_of_statements
of the loop.
An implementation could give a warning if a variable is hidden by a loop_parameter_specification
.
NOTE 3 The discrete_subtype_definition
of a for loop is elaborated just once. Use of the reserved word reverse does not alter the discrete subtype defined, so that the following iteration_scheme
s are not equivalent; the first has a null range.
for J in reverse 1 .. 0
for J in 0 .. 1
If a loop_parameter_specification
has a static discrete range, the subtype of the loop parameter is static.
Examples
17Example of a loop statement without an iteration scheme:
loop
Get(Current_Character);
exit when Current_Character = '*';
end loop;
19
Example of a loop statement with a while iteration scheme:
while Bid(N).Price < Cut_Off.Price loop
Record_Bid(Bid(N).Price);
N := N + 1;
end loop;
21
Example of a loop statement with a for iteration scheme:
for J in Buffer'Range loop -- works even with a null range
if Buffer(J) /= Space then
Put(Buffer(J));
end if;
end loop;
23
Example of a loop statement with a name:
Summation:
while Next /= Head loop -- see 3.10.1
Sum := Sum + Next.Value;
Next := Next.Succ;
end loop Summation;
25/5
Example of a simple parallel loop:
-- see 3.6
parallel
for I in Grid'Range(1) loop
Grid(I, 1) := (for all J in Grid'Range(2) => Grid(I,J) = True);
end loop;
27/5
Example of a parallel loop with a chunk specification:
declare
subtype Chunk_Number is Natural range 1 .. 8;
29/5
Partial_Sum,
Partial_Max : array (Chunk_Number) of Natural := (others => 0);
Partial_Min : array (Chunk_Number) of Natural :=
(others => Natural'Last);
30/5
begin
parallel (Chunk in Chunk_Number)
for I in Grid'Range(1) loop
declare
True_Count : constant Natural :=
[for J in Grid'Range(2) =>
(if Grid (I, J) then 1 else 0)]'Reduce("+",0);
begin
Partial_Sum (Chunk) := @ + True_Count;
Partial_Min (Chunk) := Natural'Min(@, True_Count);
Partial_Max (Chunk) := Natural'Max(@, True_Count);
end;
end loop;
31/5
Put_Line
("Total=" & Partial_Sum'Reduce("+", 0)'Image &
", Min=" & Partial_Min'Reduce(Natural'Min, Natural'Last)'Image &
", Max=" & Partial_Max'Reduce(Natural'Max, 0)'Image);
end;
32/5
For an example of an iterator_filter
, see 4.5.8.
Wording Changes from Ada 83
The constant-ness of loop parameters is specified in 3.3, “Objects and Named Numbers”.