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.


Last Updated: 10/13/2017
Posted on: 6/11/2012