foreach syntax, std.mixin

dsimcha dsimcha at yahoo.com
Sun Nov 8 17:13:54 PST 2009


== Quote from Bill Baxter (wbaxter at gmail.com)'s article
> On Sun, Nov 8, 2009 at 1:43 PM, dsimcha <dsimcha at yahoo.com> wrote:
> > == Quote from Philippe Sigaud (philippe.sigaud at gmail.com)'s article
> >> > dsimcha wrote:
> >> > Makes me wonder why noone thought of this until now, or maybe someone
> did
> >> > and I
> >> > forgot.  How's:
> >> >
> >> > foreach(fooElem, barElem; unpack(zip(foo, bar))) {}, or:
> >> >
> >> > foreach(i, elem; enumerate(chain(foo, bar))) {} ?
> >> >
> >> >
> >> Can that be done for more than two ranges? The syntax is so nice, I'd be
> >> deligthed to have that.
> >
> > Hot off the press and VERY prototype-ish:
> >
> > Code:
> > http://pastebin.com/m2087e524
> >
> > Docs:
> > http://cis.jhu.edu/~dsimcha/unpackEnumerate.html
> >
> > Does this look like a good addition to std.range?  The elegance of it i
> s it solves
> > the problem of providing syntactic sugar to ranges w/ zero ripple effects
>  either
> > in the compiler or in the rest of Phobos.  I'll file it somewhere more
> official
> > after people review it a little and refine the idea, but I definitely thi
> nk
> > something similar to this has a legit place in std.range.
> >
> > If you're wondering how unpack works and don't want to grovel through all
>  the
> > code, it's tons of string mixin magic.  That's about the only way I was
>  able to
> > make it work.
> What's the overhead like?  That would be the thing that would keep me
> from using unpack or enumerate.  As Andrei is fond of saying
> "expensive abstractions are a dime a dozen".  If it's not too bad then
> this sounds like a decent solution to me.
> --bb

import std.stdio, std.perf;

void main() {
    auto myRange = replicate(100_000_000, 0);

    scope pc = new PerformanceCounter;
    pc.start;

    foreach(i, num; enumerate(myRange)) {}

    pc.stop;
    writeln("Enumerate:  ", pc.milliseconds);

    pc.start;

    foreach(num; myRange) {}

    pc.stop;
    writeln("Raw Range:  ", pc.milliseconds);

    pc.start;
    foreach(i; 0..100_000_000) {
        int num = 0;
    }

    pc.stop;
    writeln("Plain old for loop:  ", pc.milliseconds);
}

Enumerate:  1207
Raw Range:  940
Plain old for loop:  112

In other words, it's not a zero-cost abstraction, but it's not what you'd call
expensive either, especially since in real-world code you'd actually have a loop
body.  Also, apparently LDC inlines opApply, proving that a sufficiently smart but
realistically implementable compiler can make this a zero-cost abstraction.
(Someone who uses LDC please confirm this.)

To put this in perspective, ranges are not a free abstraction on DMD either,
probably because DMD's inliner isn't sufficiently aggressive.  Really, the raw
range case shouldn't take nearly as long as it does either, as the plain old for
loop test proves.

IMHO the details of how DMD's optimizer currently works should not dictate the
design of the standard library unless either performance is absurdly bad or we
have good reason to believe that common implementations will never be any better.
 LDC proves that inlining opApply can be done.  If you have something that
absolutely must be as fast as possible now, you may not want to use this (or
ranges either), but in the bigger picture I think it's efficient enough to have a
legitimate place in Phobos.



More information about the Digitalmars-d mailing list