www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A delegate problem, create delegation in loop

reply lijie <cpunion gmail.com> writes:
Hi,

My test code:


 import std.stdio;
 void main() {
     void delegate()[] functions;
     foreach (i; 0 .. 5) {
         functions ~= {
             printf("%d\n", i);
         };

}
     foreach (func; functions) {
         func();
     }
 }

output: $ dmd DMD64 D Compiler v2.059 ... $ ./main
 5
 5
 5
 5
 5

Seems like all delegations shared a stack variable, I think delegation must copy the value into its context. I can avoid it: void delegate() createDelegate(int i) {
         void exec() {
             printf("%d\n", i);
         }
         return &exec;
     }
     foreach (i; 0 .. 5) {
         functions ~= createDelegate(i);
     }

But hope for improve it. Best regards, -- Li Jie
Jul 04 2012
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
lijie:

 import std.stdio;
 void main() {
     void delegate()[] functions;
     foreach (i; 0 .. 5) {
         functions ~= {
             printf("%d\n", i);
         };

}
     foreach (func; functions) {
         func();
     }
 }


import std.stdio; void main() { void delegate()[] functions; foreach (i; 0 .. 5) functions ~= ((int j) => { printf("%d\n", j); })(i); foreach (func; functions) func(); } Bye, bearophile
Jul 05 2012
parent lijie <cpunion gmail.com> writes:
On Thu, Jul 5, 2012 at 3:36 PM, bearophile <bearophileHUGS lycos.com> wrote:

 lijie:


  import std.stdio;
 void main() {
     void delegate()[] functions;
     foreach (i; 0 .. 5) {
         functions ~= {
             printf("%d\n", i);
         };

}
     foreach (func; functions) {
         func();
     }
 }


import std.stdio; void main() { void delegate()[] functions; foreach (i; 0 .. 5) functions ~= ((int j) => { printf("%d\n", j); })(i); foreach (func; functions) func(); }

Thanks bearophile. Best regards, -- Li Jie
Jul 05 2012
prev sibling parent reply Denis Shelomovskij <verylonglogin.reg gmail.com> writes:
05.07.2012 9:54, lijie пишет:
 Hi,

 My test code:

     import std.stdio;
     void main() {
          void delegate()[] functions;
          foreach (i; 0 .. 5) {
              functions ~= {
                  printf("%d\n", i);
              };

          }
          foreach (func; functions) {
              func();
          }
     }


 output:
     5
     5
     5
     5
     5

This program behaves as expected. Just like C# program that will give the same output: --- delegate void MyFunc(); class Program { static void Main() { var funcs = new MyFunc[5]; for (int i = 0; i < funcs.Length; ++i) funcs[i] = new MyFunc(() => System.Console.WriteLine(i)); foreach (var f in funcs) f(); } } --- because "i" is the same for every iteration. Different situation is for such C# loop: --- for (int i = 0; i < funcs.Length; ++i) { int t = i; funcs[i] = new MyFunc(() => System.Console.WriteLine(t)); } --- where "t" is local for scope. Here C# behaves correctly, but D doesn't. This D loop --- foreach(i; 0 .. 5) { int t = i; functions ~= { printf("%d\n", t); }; } --- prints "4" five times. It's Issue 2043: http://d.puremagic.com/issues/show_bug.cgi?id=2043 I'm not posting workaround here because bearophile already did it. -- Денис В. Шеломовский Denis V. Shelomovskij
Jul 05 2012
parent reply lijie <cpunion gmail.com> writes:
On Thu, Jul 5, 2012 at 4:26 PM, Denis Shelomovskij <
verylonglogin.reg gmail.com> wrote:


 Different situation is for such C# loop:
 ---
 for (int i = 0; i < funcs.Length; ++i)
 {
     int t = i;
     funcs[i] = new MyFunc(() => System.Console.WriteLine(t));
 }
 ---
 where "t" is local for scope. Here C# behaves correctly, but D doesn't.
 This D loop
 ---
 foreach(i; 0 .. 5) {
     int t = i;
     functions ~= { printf("%d\n", t); };
 }
 ---
 prints "4" five times. It's Issue 2043:
 http://d.puremagic.com/issues/**show_bug.cgi?id=2043
 <http://d.puremagic.com/issues/show_bug.cgi?id=2043>

How to distinguish which variables will be copied to the closure context? I think this is a scope rule, in the previous code, there are three variables: 1. function arguments 2. loop variables 3. local variables Seems only function parameters is copied. In C#, local variables is copied. There are other rules? And why is the loop variable not local? Thanks. Best regards, -- Li Jie
Jul 05 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/06/2012 05:14 AM, lijie wrote:
 On Thu, Jul 5, 2012 at 4:26 PM, Denis Shelomovskij
 <verylonglogin.reg gmail.com <mailto:verylonglogin.reg gmail.com>> wrote:

     Different situation is for such C# loop:
     ---
     for (int i = 0; i < funcs.Length; ++i)
     {
          int t = i;
          funcs[i] = new MyFunc(() => System.Console.WriteLine(t));
     }
     ---
     where "t" is local for scope. Here C# behaves correctly, but D
     doesn't. This D loop
     ---
     foreach(i; 0 .. 5) {
          int t = i;
          functions ~= { printf("%d\n", t); };
     }
     ---
     prints "4" five times. It's Issue 2043:
     http://d.puremagic.com/issues/__show_bug.cgi?id=2043
     <http://d.puremagic.com/issues/show_bug.cgi?id=2043>

 How to distinguish which variables will be copied to the closure context?

They are not copied, they are stored there.
 I think this is a scope rule, in the previous code, there are three
 variables:
 1. function arguments
 2. loop variables
 3. local variables
 Seems only function parameters is copied. In C#, local variables is
 copied. There are other rules? And why is the loop variable not local?

 Thanks.

 Best regards,

 -- Li Jie

It is simple. Variable declarations introduce a new variable. Closures that reference the same variable will see the same values. ---- foreach(i; 0..3) { functions~={writeln(i);}; } is the same as for(int i=0;i<3;i++) { functions~={writeln(i);}; } is the same as {int i=0;for(;i<3;i++) { functions~={writeln(i);}; }} is the same as { int i=0; { functions~={writeln(i);}; } i++; { functions~={writeln(i);}; } i++; { functions~={writeln(i);}; } i++; } ---- foreach(i; 0..3){ int j=i; functions~={writeln(j);}; } is the same as for(int i=0;i<3;i++){ int j=i; functions~={writeln(j);}; } is the same as {int i=0;for(i<3;i++){ int j=i; functions~={writeln(j);}; } is the same as { int i=0; { int j=i; functions~={writeln(j);}; } i++; { int j=i; functions~={writeln(j);}; } i++; { int j=i; functions~={writeln(j);}; } i++; } ---- I think it is quite intuitive.
Jul 06 2012
parent lijie <cpunion gmail.com> writes:
On Fri, Jul 6, 2012 at 3:06 PM, Timon Gehr <timon.gehr gmx.ch> wrote:

 It is simple. Variable declarations introduce a new variable. Closures
 that reference the same variable will see the same values.

 ----

 foreach(i; 0..3) { functions~={writeln(i);}; }

 is the same as

 for(int i=0;i<3;i++) { functions~={writeln(i);}; }

 is the same as

 {int i=0;for(;i<3;i++) { functions~={writeln(i);}; }}

 is the same as

 {
     int i=0;
     { functions~={writeln(i);}; }
     i++;
     { functions~={writeln(i);}; }
     i++;
     { functions~={writeln(i);}; }
     i++;
 }


 ----

 foreach(i; 0..3){ int j=i; functions~={writeln(j);}; }

 is the same as

 for(int i=0;i<3;i++){ int j=i; functions~={writeln(j);}; }

 is the same as

 {int i=0;for(i<3;i++){ int j=i; functions~={writeln(j);}; }

 is the same as

 {
     int i=0;
     { int j=i; functions~={writeln(j);}; }
     i++;
     { int j=i; functions~={writeln(j);}; }
     i++;
     { int j=i; functions~={writeln(j);}; }
     i++;
 }

 ----

 I think it is quite intuitive.

Understood, thanks.
Jul 06 2012