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.