More powerful foreach statements

pragma pragma_member at pathlink.com
Fri Jul 21 08:02:36 PDT 2006


In article <e9qe1i$1m5c$3 at digitaldaemon.com>, Oskar Linde says...
>
>pragma wrote:
>> In article <e9ogh7$b1b$1 at digitaldaemon.com>, kris says...
>>> Ben Phillips wrote:
>>>> While D's foreach is a much appreciated improvement over C++'s template version,
>>>> I feel there are still some ways in which it can be improved. I listed two ideas
>>>> below,
>> [snipped foreach proposal]
>> Being able to get at the current iteration would be nice, but its certainly
>> sugar.  The only way I can see this single case being covered, is with a third
>> foreach arg, like so:
>> 
>> foreach(uint i,key,value; myAA){
>> }
>> 
>> .. as this makes a rather nice, logical extension to things IMO.  But I'm not
>> holding my breath for its inclusion into D. ;)
>> 
>> A variation would be to use a specialized iterator:
>> 
>> uint i;
>> foreach(key,value; myAA.forward(&i){
>> //i is incremented for each call of opApply()
>> }
>
>or why not:
>uint i = -1;
>foreach(key,value; myAA) {
>	i++;
>	...
>}
>
>:)

Oh sure.  If you want to do things the old-fashioned way... might as well use
'goto' while you're at it. ;p


>
>>> IIRC, someone already has a framework 
>>> for such things? Was it Oskar?
>> 
>> I remember seeing a heavily templated Array library posted here, that Andrew
>> made a while back, could that be it?  It was drafted well before implicit
>> templates were available, so maybe its worth reviewing again.
>
>I also posted a templated array library suggestion earlier. It was 
>drafted just after the inclusion of implicit function template 
>instantiation and was designed to work with the current limited ifti 
>support. The code would be a lot nicer if we got a more complete ifti 
>support implemented though.
>
>The implementation and (ugly) ddoc:
>
>http://www.csc.kth.se/~ol/array.d
>http://www.csc.kth.se/~ol/array.html
>
>It doesn't currently contain any iterator functions. I've been toying 
>with that a little though and have things like these working:
>
>// pensionCosts is 4 % of the net salary of employees older than 28 yrs
>// assume Employee[] employees;
>double pensionCosts = 0;
>foreach(e; employees.select((Employee e) { return e.age >= 28; }))
>	pensionCosts += employee.salary * 0.04;
>
>Reverse iterators: (I haven't currently implemented reverse utf 
>conversions, like foreach(dchar c; "abcd"c.reverseIterator()). It is a 
>lot of work and I'm not sure they are worth it.)
>
>foreach(x; "abcd".reverseIterator())
>	writefln("x = %s",x);
>
>Python style ranges:
>
>foreach(x; range(5))
>	... x is 0,1,2,3,4
>foreach(x; range(1, 6))
>	... x is 1,2,3,4,5
>foreach(x; range(1.5, 0, -0.25)) // bad idea to support doubles?
>	... x is 1.5, 1.25, 1.0, 0.75, 0.5, 0.25
>
>I've not found a good iterator design yet though. Only supporting 
>foreach style iteration is a bit too limited.
>
>I would also like array views to work as iterators and also support 
>common array operations without having to generate temporary arrays.
>
>Currently:
>
>double pensionCosts = employees
>          .filter((Employee e) { return e.age >= 28; })
>          .map((Employee e) { return e.salary * 0.04; })
>          .sum();
>
>Will generate a temporary Employee[] array and a temporary double[] array.
>
>Using array views one could get around the temporaries:
>
>// give all employees over 55 a 3 % raise:
>
>employees.select((Employee e) { return e.age >= 55; })
>          .update((Employee e) { e.salary *= 1.03; });
>
>//(No temporaries created.)
>
>/Oskar

IMO, I think it's all *very* worth it.  It kind of fills this gap between what
phobos gives us and what STL's iterators and algorithms has to offer. 

I think that if you have an 'array view' widget/template that supports
opApply(), length(), and opIndex() for all the view types you return, then
you're pretty much set.  That will cover both the random-access and
sequential-access modes that arrays already enjoy.  That way, you can support
for() iteration as well, and still not need any temporaries. ;)

As for ranges, I wouldn't throw those out.  Ruby also has them, and thanks to
Rails, prototype.js now supports them now too.  I'd be willing to bet that
someone will come along to the DNG and say "Where's the range support?!" - and
we can all just point them to your library.  

I'd imagine ranges being an extension of your 'array view' concept.

I think something like the following would be workable:

/**/ struct ReverseArrayView(ArrayT){
/**/   ElemType!(ArrayT) opIndex(uint idx);
/**/   ArrayT opSlice(uint start,uint end); //creates temporary or another view?
/**/   int opApply(int delegate(inout ElemType!(ArrayT)) dg);
/**/   uint length();
/**/   static opCall(ArrayT arr);
/**/ }
/**/
/**/ template reverse(ArrayT){
/**/   ReverseArrayView!(ArrayT) reverse(ArrayT arr){ 
/**/     return ReverseArrayView!(ArrayT)(arr);
/**/   }
/**/ }

The trick is to get ArrayView instances to jive with your templates like
ElemType!() such that they get treated like first-class arrays where possible. I
think static if() could be used to check for a static property on ArrayViews
like "isArrayView=true", such that standard arrays will fail the test.

- EricAnderton at yahoo



More information about the Digitalmars-d mailing list