Gem #127: Iterators in Ada 2012 - Part 1
Let's get started...
The following examples assume we have instantiated an Ada list such as:
with Ada.Containers.Doubly_Linked_Lists; ... declare package Integer_Lists is new Ada.Containers.Doubly_Linked_Lists (Integer); use Integer_Lists; L : Integer_Lists.List; begin L.Append (10); L.Append (20); end;
In Ada 2005, an iteration over this list would look like:
declare C : Integer_Lists.Cursor; begin C := First (L); while Has_Element (C) loop -- Print current value Put_Line (Integer'Image (Element (C))); -- Change the element in place in the list Replace_Element (L, C, Element (C) + 1); Next (C); end loop; end;
If the list contains elements more complex than integers (controlled types for instance), the above code is not very efficient, since a call to function Element will return a copy of the element. To avoid a copy, one could use a nested subprogram and the procedures Query_Element and Update_Element, but that would make the code more complex and less readable.
Ada 2012 defines three forms of iterators. The first form is called a generalized iterator. The syntax and semantics for it is given in the Ada 2012 Reference Manual (5.5.2), but here is an example of its use:
for C in L.Iterate loop Put_Line (Integer'Image (Element (C))); Replace_Element (L, C, Element (C) + 1); end loop;
In this code, C is also a cursor just like before, but it takes on values returned by the Iterate function defined in the list instantiation. This code is basically hiding the calls to First, Has_Element, and Next.
The second form of iterator, called a container element iterator, is even more user friendly and hides the use of cursors completely. This form of iterator gives direct access to the elements of a container (see Annex A of the Ada RM for specifications of the predefined containers):
for E of L loop -- Note "of" instead of "in" here Put_Line (Integer'Image (E)); E := E + 1; end loop;
The third form of iterator, called an array component iterator, is similar to a container element iterator, but applies to array types. Here is an example of this form:
declare Arr : array (1 .. 2) of Integer := (1 => 10, 2 => 20); begin for E of Arr loop Put_Line (Integer'Image (E)); E := E + 1; -- Change in place end loop; end;
As the example shows, we can even modify the iterator element E directly, and this modifies the value in the list itself. This is also efficient code when the list contains complex types, since E is not a copy of an element, but a reference to it.
The second part of this Gem series will explain how to write your own iterators and how the loops shown above are expanded by the compiler.