通过引用捕获循环中的闭包?

Closures in loops capturing by reference?

D 中的委托似乎通过引用捕获局部值,这在循环中创建闭包时会产生奇怪的副作用:最后,您有 n 个具有相同上下文指针的闭包。举个例子:

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");
}

这会打印 NaNaNaNaNa batman.

这是预期的行为吗?如果是这样,我该如何解决它才能正确打印所有数组元素?

当使用带有计数器变量的 for 循环时,它变得更加有趣,最终 i 等于数组大小,而当在委托中使用 closures[i] 时,它会抛出一个错误边界错误。

是的,这是预期的行为(编辑:...它实际上是一个古老的已知错误!https://issues.dlang.org/show_bug.cgi?id=2043 所以只是预期它会发生并且您可以轻松地习惯它,但它实际上不是' supposed to happen) 并且在其他语言中也可以看到,所以这是一个很好的原则。

要获取循环中变量的单独副本,请调用另一个函数,returns 要存储的委托,将循环变量传递给它。

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");
}

clojures ~= ((b) => { write(b);})(a); 行已更改:它定义了一个 returns 委托的快速委托。额外的函数返回函数关闭循环状态的快照,而不仅仅是函数级局部变量。

我也在 JavaScript 中经常使用它:

 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]));

这是出于与 D 相同的原因而完成的同一件事,只是语法不同,并且分解为更大的函数,而不是试图将其作为一个单行代码来完成。

我们定义了一个函数,其中 returns 一个使用捕获的循环变量的函数。在使用点,我们调用一个函数,returns 为以后存储的委托。

在shorthand D语法中,((b) => { write(b);})(a);(b) => ...就是javascript中看到的makeHandler函数。 { write(b); } 它 returns 是 shorthand for return function() { ... } 在 JS 中(顺便说一句,相同的 JS 语法基本上也适用于离子 D,你可以用 [=18= 写一个手写的东西] 或 function 关键字。尽管 D 的 function 不捕获变量,delegate 会捕获变量..)

然后,最后,它周围的括号和末尾的 (a) 只是调用函数。里面的东西和makeHandler是一样的,(...)(a)叫它;它是 makeHadndler(a).