associative arrays: iteration is finally here

Steven Schveighoffer schveiguy at yahoo.com
Thu Oct 29 06:22:45 PDT 2009


On Wed, 28 Oct 2009 10:22:00 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> Walter has magically converted his work on T[new] into work on making  
> associative arrays true templates defined in druntime and not considered  
> very special by the compiler.
>
> This is very exciting because it opens up or simplifies a number of  
> possibilities. One is that of implementing true iteration. I actually  
> managed to implement last night something that allows you to do:
>
> int[int] aa = [ 1:1 ];
> auto iter = aa.each;
> writeln(iter.front.key);
> writeln(iter.front.value);
>
> Two other iterations are possible: by key and by value (in those cases  
> iter.front just returns a key or a value).
>
> One question is, what names should these bear? I am thinking of makign  
> opSlice() a universal method of getting the "all" iterator, a default  
> that every container must implement.
>
> For AAs, there would be a "iterate keys" and "iterate values" properties  
> or functions. How should they be called?

This is one of the benefits of opApply.

Rather than define some new names to make up for the deficiencies of  
range-foreach (especially so near the release of D2), why not focus on  
fixing the deficiencies?

i.e. here is my desired interface:

foreach(key, value; aa) // iterate both keys and values
foreach(value; aa) // default to iterating values (to be consistent with  
arrays)
foreach(key; aa.keys) // iterate keys lazily.

If ranges cannot support this interface, I think the range paradigm needs  
some work.  opApply works great for this since the delegate type defines  
the desired interface.  The problem with ranges is the potential "opRange"  
method in it's natural form should overload on the return value, and have  
no parameters.

Here's a possible idea: make foreach(X x, Y y, Z z,...; container)  
translate to foreach(X x, Y y, Z z,...; container.opRange!(X, Y, Z,  
...)()).  In the case of inferred types, maybe you can do something like:

opRange!(T : keytype, U : valuetype)() and have the compiler infer types  
for x and y as keytype and valuetype.

The only drawback I see is opRange can't be virtual.  However, how does  
one define a "virtual" range return, when a range is invariably a struct?   
It might be a moot issue.

Of course, you could always meet the virtual requirement by naming your  
range-generating method...

So with my scheme, the AA becomes:

struct AA(Key, Val)
{
   ... // implementation
   auto opRange(T : Key, U : Val)() { ... }
   auto opRange(T : Val)() { ... }
   auto keys() { ... }
}

One other thing left to define is what foreach calls to get the key(s) in  
the case of multiple arguments to foreach.  The value is generally  
accessed via front().  Might as well tackle the ref possibilities too :)

-Steve



More information about the Digitalmars-d mailing list