6.16 Iterators

If a structure a has a member function operator iter(), the code

for(var i : a) {
  <statements>
}

is syntactic sugar for

for(var it=a.operator iter(); it.valid(); it.advance()) {
  var i=it.get();
  <statements>
}

Thus, we can make a structure iterable by defining an operator iter method that returns an object with the following methods:

T get()

returns the value at the iterator’s current position (without changing the position). Note that T can be any type, builtin or user-defined;

void advance()

advances the iterator to the next position;

bool valid()

returns true if the iterator is at a valid position or false if the iterator has advanced past the last item.

Although the return type of operator iter can theoretically be any type that has these three methods, it is strongly recommended to use the Iter_T structure defined in the templated module collections.iter(T) so that other utilities (see Iterators and utilities) can be used with the iterator. The three methods of this structure can be set as fields.

As an example, here is an iterator for the even-numbered elements of an array of strings:

from collections.iter(T=string) access
    Iter_T as Iter_string,
    Iterable_T as Iterable_string;
struct EvenStrings {
  string[] a;
  void operator init(string[] a) {
    this.a=a;
  }
  Iter_string operator iter() {
    int i = 0;
    Iter_string result;
    result.get = new string() { return a[i]; };
    result.advance = new void() { i += 2; };
    result.valid = new bool() { return i < a.length; };
    return result;
  }
  autounravel Iterable_string operator cast(EvenStrings es) {
    Iterable_string result;
    result.operator iter = es.operator iter;
    return result;
  }
}
string[] a = {'a', 'b', 'c', 'd', 'e', 'f'};
for (string s : EvenStrings(a)) {
  write(s);
}

Running this code prints out the following:

a
c
e

The autounraveled (see Autounravel) implicit cast to Iterable_string is not strictly necessary, but it allows the use of iterable utilities; see Iterators and utilities. This enables code like the following, which uses the enumerate utility to attach counters to the iterated strings:

from collections.enumerate(T=string) access enumerate;
for (var kv : enumerate(EvenStrings(a))) {
  write(string(kv.k) + ": " + kv.v);
}

Running this code prints out the following:

0: a
1: c
2: e