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.