Looped closure captured by reference?

Delegates from D seem to capture local values ​​by reference, which has strange side effects when creating closures in loops: at the end, you have n closures with equal context pointers. Take this example:

import std.stdio;
alias Closure = void delegate();
Closure[] closures;

void main(){
    foreach(a; ["Je", "Tu", "En", "Oui", "Na"])
        closures ~= {write(a);};
    foreach(c; closures)
        c();
    writeln(" batman");
}

      

Will print NaNaNaNaNa batman

.

Is this the expected behavior? If so, how would I work on it so that it prints all array elements correctly?

It gets even funnier when using a for-loop with a counter variable, at the end i

is equal to the size of the array, and when used closures[i]

in a delegate, it throws an out-of-bounds error.

+3


source to share


1 answer


Yes, this is the expected behavior (EDIT: ... this is a really old known bug! Https://issues.dlang.org/show_bug.cgi?id=2043 so only expected this happens in this and you can easily get used to it but it shouldn't actually happen) and also seen in other languages, so it's a good principle to know.

To get a separate copy of the variables in the loop, call another function that returns the delegate you want to store by passing the loop variable to it.

import std.stdio;
alias Clojure = void delegate();
Clojure[] clojures;

void main(){
    foreach(a; ["Je", "Tu", "En", "Oui", "Na"])
        clojures ~= ((b) => { write(b);})(a);
    foreach(c; clojures)
        c();
    writeln(" batman");
}

      

The line clojures ~= ((b) => { write(b);})(a);

has changed: This defines a swift delegate that the delegate returns. The optional return function closes behind a snapshot of the loop state, not just function-level local variables.

I use a lot in JavaScript too:



 function makeHandler(item) {
      return function() {
         // use item here
      };
 }

var array = [1,2,3];
for(var I = 0; I < array.length; I++)
   foo.addEventListener("click", makeHandler(array[I]));

      

It does the same thing for the same reason as D, only in a different syntax, and breaks up into larger functions instead of trying to do it as a one-liner.

Let's define a function that returns a function that uses the captured loop variable. At the point of use, we call one function that returns a delegate that is stored later.

The syntax reduced D, ((b) => { write(b);})(a);

, (b) => ...

- a feature makeHandler

visible in javascript. The return value { write(b); }

is shorthand for return function() { ... }

in JS (BTW that the same JS syntax basically works with a different D, you can write a long thing with keywords delegate

or function

. D function

doesn't capture variables, though, delegate

it does.)

Then finally the parentheses around it and (a)

at the end are just a function call. The content inside is the same as makeHandler

calling it (...)(a)

; it is makeHadndler(a)

.

+3


source







All Articles