This version of the for construct loops
over an E collection, matching both the key-pattern
and the value-pattern against successive key-value
pairs from the collection. Variables defined in either pattern are available
to the body-expr, which is evaluated once for
each matching key-value pair.

Again, in common usage the key-pattern and value-pattern
can be just variable names, which will get bound to each of the key-value
pairs in the entire collection.

As we can see, the keys of a list are the indices into the list. More
interestingly, E's map collections are hash tables with arbitrary keys
and values. A map can also be constructed with square brackets, but instead
of writing single elements we write key-value pairs separated by =>.
As a silly example, let's suppose we want to concatenate together all
the names of capital cities of states whose names include the letter "e":

But since the body of the loop is only executed if both pattern matches
succeed, we can move tests up into these patterns. Since the above loop
makes no other use of the "state" variable, it can be rewritten this
way:

The key-pattern in this last example will only
match strings containing the letter "e".

Custom Collections

Besides conventional collection classes (mutable and immutable arrays
and hashtables), a variety of other objects can also be treated as collections
of key-value pairs, and can be iterated over with a for
loop. In the Jabberwocky example, we saw that E can treat a normal text
file (as well as a URL object) as a collection of line-number => line
pairs, and a directory as a collection of file-name => file
pairs. How can the E programmer make new kinds of objects over which
the for loop can iterate?

When the for loop iterates over a collection,
it calls its iterate(assocFunc) method, passing
in as assocFunc a function of two arguments.
To make new objects work with for, we
need to define an iterate method that applies this assocFunc
to all the things we wish to appear as key-value pairs in the collection.

So, for example, let's say we get tired of using recursion to get to
all the files at the leaves of a directory tree. Rather, we'd like to
create an object that acts like a simple collection of these leaves.
We can accomplish this with the following function:

Now leaves(filedir) defines an object leafCollection
that acts like a collection of the leaves of the directory tree rooted
at filedir. Since this def
is the last thing to be evaluated in leaves, this object
is returned. Our definition of its iterate(assocFunc)
method makes this collection suitable for use in a for
loop.

For example, I got curious about how many Java files I had in a particular
directory tree: