www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - D: Unexpected output when using a delegate and EnumMembers

reply Travis Gockel <travis gockelhut.com> writes:
I have some rather odd behavior in my D program that I've narrowed down to this:

    import std.algorithm;
    import std.stdio;
    import std.traits;

    enum E { a, b, c };

    struct S { E e; };

    void main()
    {
        immutable(S)[] source = [ S(E.a), S(E.a), S(E.b) ];
        foreach (e; EnumMembers!E)
        {
            size_t c = count!(x => x.e == e)(source);
            writeln(e, " -> ", c);
        }
    }

I would expect the output of this program to be something along the lines of:

    a -> 2
    b -> 1
    c -> 0

But the actual result is:

    a -> 2
    b -> 2
    c -> 2

Curiously, changing the for loop to foreach (e; [ E.a, E.b, E.c ]) produces my
expected output. Using foreach (e; [ EnumMembers!E ]) also produces my
expected result, so clearly my use of the range from EnumMemebers is the
problem here...I just don't know why.

By moving the count call to a separate function:

    size_t counte(Range)(E e, Range src)
    {
        return count!(x => x.e == e)(src);
    }

and changing c's initialization to size_t c = counte(e, source);, the program
works as I would expect.

I am clearly doing something wrong, but I have no idea what and would
appreciate some insight.

My compiler is DMD64 D Compiler v2.059 on Linux.
begin 644 enum_delegate.d
M(R$O=7-R+V)I;B]R9&UD" II;7!O<G0 <W1D+F%L9V]R:71H;3L*:6UP;W)T
M('-T9"YS=&1I;SL*:6UP;W)T('-T9"YT<F%I=',[" IE;G5M($4 >R!A+"!B
M+"!C('T[" IS=')U8W0 4R![($4 93L ?3L*"G-I>F5?="!C;W5N=&4H4F%N
M9V4I*$4 92P 4F%N9V4 <W)C*0I["B` ("!R971U<FX 8V]U;G0A*'  /3X 
M>"YE(#T](&4I*'-R8RD["GT*"G9O:60 ;6%I;B I"GL*("` (&EM;75T86)L
M92A3*5M=('-O=7)C92`](%L 4RA%+F$I+"!3*$4N82DL(%,H12YB*2!=.PH 
M("` 9F]R96%C:"`H93L 16YU;4UE;6)E<G,A12D*("` ('L*("` ("` ("!V
M97)S:6]N("AC;W5N=&4I"B` ("` ("` ("` ('-I>F5?="!C(#T 8V]U;G1E
M*&4L('-O=7)C92D["B` ("` ("` 96QS90H ("` ("` ("` ("!S:7IE7W0 
M8R`](&-O=6YT(2AX(#T^(' N92`]/2!E*2AS;W5R8V4I.PH ("` ("` ('=R
<:71E;&XH92P (B`M/B`B+"!C*3L*("` ('T*?0``
`
end
Jun 19 2012
next sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 06/19/12 16:44, Travis Gockel wrote:
     import std.algorithm;
     import std.stdio;
     import std.traits;
 
     enum E { a, b, c };
 
     struct S { E e; };
 
     void main()
     {
         immutable(S)[] source = [ S(E.a), S(E.a), S(E.b) ];
         foreach (e; EnumMembers!E)
         {
             size_t c = count!(x => x.e == e)(source);
             writeln(e, " -> ", c);
         }
     }
 
 I would expect the output of this program to be something along the lines of:
 
     a -> 2
     b -> 1
     c -> 0
 
 But the actual result is:
 
     a -> 2
     b -> 2
     c -> 2
 
 Curiously, changing the for loop to foreach (e; [ E.a, E.b, E.c ]) produces my
 expected output. Using foreach (e; [ EnumMembers!E ]) also produces my
 expected result, so clearly my use of the range from EnumMemebers is the
 problem here...I just don't know why.
 
 By moving the count call to a separate function:
 
     size_t counte(Range)(E e, Range src)
     {
         return count!(x => x.e == e)(src);
     }
 
 and changing c's initialization to size_t c = counte(e, source);, the program
 works as I would expect.
 
 I am clearly doing something wrong, but I have no idea what and would
 appreciate some insight.  
Yes, it can be surprising, but I'm not convinced it's actually wrong behavior (the bug is http://d.puremagic.com/issues/show_bug.cgi?id=2043) Just do this: size_t c = count!(function(x, e) { return x.e == e;} )(source, e); and it will work. [1] artur [1] I don't do that new kinky lambda syntax, sorry. ;)
Jun 19 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 06/19/2012 05:08 PM, Artur Skawina wrote:
 Yes, it can be surprising, but I'm not convinced it's actually wrong
 behavior (the bug is http://d.puremagic.com/issues/show_bug.cgi?id=2043)
It is not this bug. (And what is listed there is clearly wrong behaviour, because it can be used to break the type system.)
 Just do this:

     size_t c = count!(function(x, e) { return x.e == e;} )(source, e);

 and it will work. [1]

 artur

 [1] I don't do that new kinky lambda syntax, sorry. ;)
Your embarrassment about this issue is justifiable.
Jun 19 2012
parent Artur Skawina <art.08.09 gmail.com> writes:
On 06/19/12 17:32, Timon Gehr wrote:
 On 06/19/2012 05:08 PM, Artur Skawina wrote:
 Yes, it can be surprising, but I'm not convinced it's actually wrong
 behavior (the bug is http://d.puremagic.com/issues/show_bug.cgi?id=2043)
It is not this bug. (And what is listed there is clearly wrong behaviour, because it can be used to break the type system.)
It's not that simple. I remember considering the alternatives when I originally ran into this, and they have problems too. The "static foreach" case may be special, possibly.
 [1] I don't do that new kinky lambda syntax, sorry. ;)
Your embarrassment about this issue is justifiable.
I'm proud of it. ;) But maybe it has something to do with the fact that my compiler doesn't support them... artur
Jun 19 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 06/19/2012 04:44 PM, Travis Gockel wrote:
 I am clearly doing something wrong,
You are not.
 but I have no idea what and would
 appreciate some insight.
You have found a bug in DMD. Reduced test case that should compile: template Seq(T...){alias T Seq;} auto exec(alias a)(){return a();} void main(){ foreach(e; Seq!(0, 1)) static assert(exec!(()=>e)()==e); } You can report the bug here: http://d.puremagic.com/issues/ The 'exec' template is instantiated only once instead of two times.
Jun 19 2012
parent Travis Gockel <travis gockelhut.com> writes:
== Quote from Timon Gehr (timon.gehr gmx.ch)'s article
 You can report the bug here: http://d.puremagic.com/issues/
 The 'exec' template is instantiated only once instead of two times.
Reported: http://d.puremagic.com/issues/show_bug.cgi?id=8267
Jun 19 2012