Справочник по Ada (Ada 2022)
5.5 Операторы цикла
Оператор loop_statement включает последовательность операторов, которые должны выполняться многократно, ноль или более раз, причём итерации могут выполняться последовательно или параллельно друг другу.
Синтаксис
loop_statement ::=
[loop_statement_identifier:]
[iteration_scheme] loop
sequence_of_statements
end loop [loop_identifier];
[loop_statement_identifier:]
[iteration_scheme] loop
sequence_of_statements
end loop [loop_identifier];
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
| 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
chunk_specification ::=
integer_simple_expression
| defining_identifier in discrete_subtype_definition
integer_simple_expression
| defining_identifier in discrete_subtype_definition
loop_parameter_specification ::=
defining_identifier in [reverse] discrete_subtype_definition
[iterator_filter]
defining_identifier in [reverse] discrete_subtype_definition
[iterator_filter]
Если оператор loop_statement имеет loop_statement_identifier, то идентификатор должен повторяться после end loop; в противном случае, идентификатор после end loop отсутствует.
В схеме итераций iteration_scheme, которая начинается со служебного слова parallel, служебное слово reverse не должно присутствовать в спецификации параметра цикла loop_parameter_specification.
Правила разрешения имён
В chunk_specification, являющейся integer_simple_expression, ожидается, что integer_simple_expression будет любого целочисленного типа.
Статические семантики
Спецификация параметра цикла loop_parameter_specification объявляет параметр цикла, который является объектом, подтип (и номинальный подтип) которого определяются discrete_subtype_definition.
В chunk_specification, содержащем discrete_subtype_definition, chunk_specification объявляет объект параметра куска, подтип (и номинальный подтип) которого определяются discrete_subtype_definition.
Динамические семантики
Фильтр итерационного конструктора (loop_parameter_specification, iterator_specification или procedural_iterator) считается удовлетворяющим, когда для итерационного конструктора нет фильтра или когда условие фильтра condition оценивается как Истина для данной итерации итерационного конструктора.
Если последовательность операторов sequence_of_statements оператора цикла loop_statement с итерационным конструктором считается условно выполняемым, то операторы выполняются только тогда, когда фильтр итерационного конструктора удовлетворяется.
Итераторы цикла loop_parameter_specification и iterator_specification также могут использоваться в контекстах, отличных от операторов цикла loop_statement (например, см. 4.3.5 и 4.5.8). В таком контексте итератор условно генерирует значения в указанном ниже или в 5.5.2 порядке. Генерируемые значения — это значения, присваиваемые параметру цикла, когда фильтр итерационного конструктора удовлетворяется для этого значения. Значение не генерируется, когда условие condition фильтра iterator_filter оценивается как Ложь.
При выполнении оператора loop_statement последовательность операторов sequence_of_statements выполняется ноль или более раз, до тех пор, пока оператор loop_statement не будет завершён. Оператор loop_statement считается завершённым, когда происходит передача управления, которая переводит управление за пределы цикла, или, в случае схемы итераций, как указано ниже.
При выполнении оператора loop_statement со схемой итераций while iteration_scheme условие оценивается перед каждым выполнением последовательности операторов sequence_of_statements; если значение условия condition равно Истина, то последовательность операторов выполняется; если Ложь, то выполнение оператора loop_statement завершается.
Если служебное слово parallel присутствует в схеме итераций iteration_scheme оператора цикла loop_statement (параллельный цикл), итерации разбиваются на один или несколько кусков, каждый из которых имеет свою отдельную логическую нить управления (см. раздел 9). Если chunk_specification присутствует в параллельном цикле, то он интерпретируется первым, а результат интерпретации определяет максимальное количество кусков, используемых для параллельного цикла. Если chunk_specification является integer_simple_expression, то интерпретация выражения определяет максимальное количество кусков. Если присутствует discrete_subtype_definition, то интерпретация discrete_subtype_definition определяет подтип параметра куска, а количество значений в этом подтипе определяет максимальное количество кусков. После интерпретации chunk_specification проверяется, что определённое максимальное количество кусков больше нуля. Если проверка не пройдена, генерируется Program_Error.
Для выполнения цикла, имеющего схему_итерации, включающую указание_параметра_цикла, после разработки указания_блока и указания_аспекта, если таковые имеются, разрабатывается указание_параметра_цикла. Это разрабатывает определение_дискретного_подтипа, которое определяет подтип параметра цикла. Если определение_дискретного_подтипа определяет подтип с нулевым диапазоном, выполнение цикла завершается. В противном случае последовательность_выражений условно выполняется один раз для каждого значения дискретного подтипа, определенного определением_дискретного_подтипа, которое удовлетворяет предикатам подтипа (или до тех пор, пока цикл не будет прерван в результате передачи управления). Перед каждой такой итерацией соответствующее значение дискретного подтипа присваивается параметру цикла, связанному с данной итерацией. Если цикл является параллельным, каждый блок имеет свою собственную логическую нить управления со своей собственной копией параметра цикла; в противном случае (последовательный цикл) одна логическая нить управления выполняет цикл, и существует одна копия параметра цикла. Каждая логическая нить управления обрабатывает отдельный поддиапазон значений подтипа параметра цикла таким образом, что все значения покрываются без перекрытий. В рамках каждой логической нити управления значения присваиваются параметру цикла в порядке возрастания, если нет зарезервированного слова reverse, в противном случае значения присваиваются в порядке убывания. При отсутствии передачи управления связанная параллельная конструкция указания_параметра_цикла завершается, когда завершаются все ее логические потоки управления.
Если присутствует указание_блока с определением_дискретного_подтипа, то логическая нить управления, связанная с данным блоком, имеет свою собственную копию параметра блока, инициализированную с различным значением из дискретного подтипа, определенного определением_дискретного_подтипа. Значения параметров блока присваиваются таким образом, что они возрастают с увеличением значений диапазонов, покрываемых соответствующими параметрами цикла.
Независимо от наличия указания_блока в параллельном цикле, общее количество итераций цикла представляет собой верхнюю границу числа логических потоков управления, выделенных для цикла.
Подробности выполнения цикла со схемой_итерации, включающей указание_итератора, см. 5.5.2. Подробности, относящиеся к процедурному_итератору, см. 5.5.3.
ПРИМЕЧАНИЕ 1 Параметр цикла, объявленный указанием_параметра_цикла, является константой; его нельзя обновлять в последовательности_выражений цикла (см. 3.3).
ПРИМЕЧАНИЕ 2 Отдельное объявление_объекта для параметра цикла не требуется, так как параметр цикла автоматически объявляется указанием_параметра_цикла. Область действия параметра цикла простирается от указания_параметра_цикла до конца цикла, и правила видимости таковы, что параметр цикла виден только внутри последовательности_выражений цикла.
ПРИМЕЧАНИЕ 3 Определение_дискретного_подтипа цикла for разрабатывается только один раз. Использование ключевого слова reverse не изменяет определенный дискретный подтип, так что следующие схемы_итерации не эквивалентны; в первом случае диапазон пустой.
for J in reverse 1 .. 0
for J in 0 .. 1
for J in 0 .. 1
Примеры
Пример оператора цикла без схемы итерации:
loop
Get(Current_Character);
exit when Current_Character = '*';
end loop;
Get(Current_Character);
exit when Current_Character = '*';
end loop;
Пример оператора цикла со схемой итерации while:
while Bid(N).Price < Cut_Off.Price loop
Record_Bid(Bid(N).Price);
N := N + 1;
end loop;
Record_Bid(Bid(N).Price);
N := N + 1;
end loop;
Пример оператора цикла со схемой итерации for:
for J in Buffer'Range loop -- работает даже с пустым диапазоном
if Buffer(J) /= Space then
Put(Buffer(J));
end if;
end loop;
if Buffer(J) /= Space then
Put(Buffer(J));
end if;
end loop;
Пример оператора цикла с именем:
Summation:
while Next /= Head loop -- см. 3.10.1
Sum := Sum + Next.Value;
Next := Next.Succ;
end loop Summation;
while Next /= Head loop -- см. 3.10.1
Sum := Sum + Next.Value;
Next := Next.Succ;
end loop Summation;
Пример простого параллельного цикла:
-- см. 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;
parallel
for I in Grid'Range(1) loop
Grid(I, 1) := (for all J in Grid'Range(2) => Grid(I,J) = True);
end loop;
Пример параллельного цикла с указанием блока:
declare
subtype Chunk_Number is Natural range 1 .. 8;
subtype Chunk_Number is Natural range 1 .. 8;
Partial_Sum,
Partial_Max : array (Chunk_Number) of Natural := (others => 0);
Partial_Min : array (Chunk_Number) of Natural :=
(others => Natural'Last);
Partial_Max : array (Chunk_Number) of Natural := (others => 0);
Partial_Min : array (Chunk_Number) of Natural :=
(others => Natural'Last);
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;
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;
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;
("Total=" & Partial_Sum'Reduce("+", 0)'Image &
", Min=" & Partial_Min'Reduce(Natural'Min, Natural'Last)'Image &
", Max=" & Partial_Max'Reduce(Natural'Max, 0)'Image);
end;
Для примера фильтра_итератора, см. 4.5.8.