Справочник по Ada (Ada 2022)
5.5.3 Процедурные итераторы
Процедурный итератор (procedural_iterator) вызывает определяемую пользователем процедуру, передавая в неё тело окружающего оператора цикла (loop_statement) в качестве параметра анонимного типа доступа к процедуре, чтобы позволить телу цикла выполняться повторно как часть вызова определяемой пользователем процедуры.
Синтаксис
procedural_iterator ::=
iterator_parameter_specification of iterator_procedure_call
[iterator_filter]
iterator_parameter_specification of iterator_procedure_call
[iterator_filter]
iterator_procedure_call ::=
имя_процедурыname
| префикс_процедурыprefix iterator_actual_parameter_part
имя_процедурыname
| префикс_процедурыprefix iterator_actual_parameter_part
iterator_actual_parameter_part ::=
(iterator_parameter_association {, iterator_parameter_association})
(iterator_parameter_association {, iterator_parameter_association})
В iterator_actual_parameter_part может быть не более одного iterator_parameter_association, являющегося parameter_association_with_box.
Правила разрешения имён
Идентификатор (name) или префикс (prefix) в iterator_procedure_call должны ссылаться на вызываемый элемент C (процедура итерации), являющийся процедурой или входом, переименованным как (рассматриваемым как) процедура. При наличии iterator_actual_parameter_part, префикс может быть неявным разыменованием значения типа доступа к подпрограмме.
Вызов iterator_procedure_call без parameter_association_with_box эквивалентен вызову с iterator_actual_parameter_part, дополненным parameter_association_with_box в конце, где имя формального параметра (selector_name) определяет последний формальный параметр вызываемого элемента, обозначенного именем (name) или префиксом (prefix).
В iterator_procedure_call должно быть не более одного iterator_parameter_association для каждого формального параметра вызываемого элемента C. Каждый формальный параметр без iterator_parameter_association должен иметь значение по умолчанию (default_expression) в профиле представления C, обозначенного именем (name) или префиксом (prefix).
Формальный параметр вызываемого элемента C, связанный с parameter_association_with_box, должен быть анонимного типа доступа к процедуре A.
Правила легальности
Анонимный тип доступа к процедуре A должен иметь как минимум один формальный параметр в своём профиле параметров. Если iterator_parameter_specification является formal_part, то этот formal_part должен быть совместимым по виду с A. Если iterator_parameter_specification представляет собой список идентификаторов (defining_identifier), то количество формальных параметров A должно совпадать с длиной этого списка.
Если имя (name) или префикс (prefix) в iterator_procedure_call обозначает абстрактную подпрограмму, эта подпрограмма должна быть подпрограммой с пересылкой.
Статическая семантика
Оператор цикла (loop_statement) с схемой итерации (iteration_scheme), содержащей процедурный итератор (procedural_iterator), эквивалентен локальному объявлению процедуры P, за которым следует оператор вызова процедуры (procedure_call_statement), сформированный из iterator_procedure_call путём замены <> в parameter_association_with_box на P'Access. Часть формальных параметров (formal_part) локально объявленной процедуры P формируется из формальной части анонимного типа доступа к процедуре A, заменяя имя (identifier) каждого формального параметра этой части формального параметра или элемента списка идентификаторов (defining_identifier), заданных в iterator_parameter_specification. Тело P состоит из условно выполняемого набора операторов (sequence_of_statements). Процедура P называется процедурой тела цикла.
В процедурном итераторе аспект Parallel_Calls (см. 9.10.1) процедуры тела цикла равен True, если в соответствующем операторе цикла присутствует ключевое слово parallel, и False в противном случае.
Для вызываемого элемента S, имеющего ровно один формальный параметр анонимного типа доступа к подпрограмме, могут быть указаны следующие аспекты:
Allows_Exit
Аспект Allows_Exit имеет тип Boolean. Указанное значение должно быть статическим. Аспект Allows_Exit унаследованной примитивной подпрограммы равен True, если Allows_Exit равен True либо для соответствующей подпрограммы типа-предка, либо для любой другой унаследованной подпрограммы, которую она переопределяет. Если не указан или унаследован как True, аспект Allows_Exit вызываемого элемента равен False. Для входа разрешено только явное указание False для аспекта Allows_Exit.
Указание аспекта Allows_Exit как True для подпрограммы означает, что подпрограмма разрешает выход, то есть она готова к завершению произвольными передачами управления из процедуры тела цикла, включая распространение исключений. Подпрограмма, для которой Allows_Exit равен True, должна использовать завершение (finalization) по мере необходимости, а не обработку исключений, для освобождения ресурсов и внесения любых необходимых финальных обновлений в структуры данных.
Parallel_Iterator
Аспект Parallel_Iterator имеет тип Boolean. Указанное значение должно быть статическим. Аспект Parallel_Iterator унаследованной примитивной подпрограммы равен True, если Parallel_Iterator равен True либо для соответствующей подпрограммы типа-предка, либо для любой другой унаследованной подпрограммы, которую она переопределяет. Если не указан или унаследован как True, аспект Parallel_Iterator вызываемого элемента равен False.
Указание аспекта Parallel_Iterator как True для вызываемого элемента означает, что элемент разрешает вызывать процедуру тела цикла из нескольких различных логических потоков управления. Аспект Parallel_Iterator для подпрограммы должен быть статически False, если подпрограмма позволяет выход.
Правила легальности
Если вызываемый объект переопределяет унаследованную подпрограмму диспетчеризации, допускающую выход, то переопределяемый вызываемый объект также должен допускать выход. Если вызываемый объект переопределяет унаследованную подпрограмму диспетчеризации, имеющую аспект Parallel_Iterator со значением True, то переопределяемый вызываемый объект также должен иметь аспект Parallel_Iterator со значением True.
Оператор цикла с процедурным_итератором в качестве схемы_итерации должен начинаться со служебного слова parallel тогда и только тогда, когда вызываемый объект, идентифицированный в вызове_процедуры_итератора, имеет аспект Parallel_iterator со значением True.
Если фактический параметр анонимного типа доступа к подпрограмме, переданный в явном вызове подпрограммы, для которой аспект Parallel_Iterator равен True, имеет вид P'Access, то у подпрограммы P должен быть аспект Parallel_Calls со значением True (см. 9.10.1).
Последовательность_операторов оператора цикла с процедурным_итератором в качестве схемы_итерации должна содержать оператор выхода, оператор возврата, оператор перехода или оператор переповтор, которые покидают цикл только в том случае, если вызываемый объект, связанный с процедурным_итератором, позволяет выход.
Последовательность_операторов оператора цикла с процедурным_итератором в качестве схемы_итерации не должна содержать оператор приёма, объявление_входа которого находится вне оператора цикла.
Динамическая семантика
Для выполнения оператора цикла со схемой_итерации, имеющей процедурный_итератор, вызывается процедура, обозначенная именем или префиксом в вызове_процедуры_итератора (итерационная процедура), передавая в качестве параметра значение доступа к процедуре тела цикла. Затем итерационная процедура вызывает процедуру тела цикла ноль или более раз и возвращается, после чего оператор цикла завершается. Если присутствует служебное слово parallel, итерационная процедура может вызывать процедуру тела цикла из нескольких различных логических потоков управления. Спецификация_аспекта, если она есть, обрабатывается до вызова итерационной процедуры.
Ограниченные (временно́й) ошибки
Если вызываемый объект, идентифицированный в вызове_процедуры_итератора, допускает выход, то ошибкой является вызов процедуры тела цикла из внутри операции отложенного прерывания (см. 9.8), за исключением случая, когда весь оператор цикла находился внутри той же операции отложенного прерывания. При обнаружении ошибки генерируется исключение Program_Error в точке вызова; в противном случае передача управления из последовательности_операторов оператора цикла не обязательно завершит оператор цикла, и процедура тела цикла может быть вызвана снова.
Если оператор цикла с процедурным_итератором в качестве схемы_итерации (см. 5.5) не начинается со служебного слова parallel, то ошибкой является вызов процедуры тела цикла из другого логического потока управления, чем тот, который инициирует оператор цикла. При обнаружении ошибки генерируется исключение Program_Error; в противном случае могут возникнуть конфликты, связанные с одновременным выполнением процедуры тела цикла, которые не будут обнаружены политикой проверки конфликтов (см. 9.10.1). Кроме того, распространение исключения или попытка выхода в присутствии нескольких потоков управления не обязательно завершит оператор цикла, может возникнуть тупик или процедура тела цикла может быть вызвана снова.
Примеры
Пример итерации по карте от My_Key_Type к My_Element_Type (см. A.18.4):
-- Приведённое выше эквивалентно:
Пример итерации по переменным среды (см. A.17):
for (Name, Val) of Ada.Environment_Variables.Iterate(<>) loop
-- "(<>)" необязательно, так как это последний параметр
Put_Line (Name & " => " & Val);
end loop;
-- "(<>)" необязательно, так как это последний параметр
Put_Line (Name & " => " & Val);
end loop;
-- Приведённое выше эквивалентно:
declare
procedure P (Name : String; Val : String) is
begin
Put_Line (Name & " => " & Val);
end P;
begin
Ada.Environment_Variables.Iterate (P'Access);
end;
procedure P (Name : String; Val : String) is
begin
Put_Line (Name & " => " & Val);
end P;
begin
Ada.Environment_Variables.Iterate (P'Access);
end;