More powerful foreach statements
Oskar Linde
oskar.lindeREM at OVEgmail.com
Fri Jul 21 10:01:04 PDT 2006
pragma wrote:
> In article <e9qe1i$1m5c$3 at digitaldaemon.com>, Oskar Linde says...
>> 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. ;)
Yes, that is probably the right way do to it. If the views support the
same operations as arrays do, they will be usable just as arrays, and
views-of-views and similar would work transparently.
> 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.
Yes, ideally, a range should work just like a (read only) array, but
without allocating any memory. Silly example:
range(100).select((int x) { return x % 5 == 0 }).sum()
//returns 950
> 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.
Yes, that is definitely doable. The only problem is that there is no way
to "inject" methods for anything but built-in arrays. I'd have to add
wrappers to all views for all functions (a mixin will probably be
enough), or live with calling them as ordinary functions:
update(select(employees,
(Employee e) { return e.age >= 55; }),
(Employee e) { e.salary *= 1.03; });
instead of:
employees.select((Employee e) { return e.age >= 55; })
.update((Employee e) { e.salary *= 1.03; });
It would be nice to have a way to inject methods/define extension
methods for other types than arrays. Maybe something like what has been
proposed before:
int squared(this int x) { return x*x; }
and called:
5.squared();
Just add ifti template support to this and the array functions would be
applicable as methods of any type that implements the needed iterator
semantics (opApply or opIndex and length()).
/Oskar
More information about the Digitalmars-d
mailing list