通过引用捕获循环中的闭包?
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)
.
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)
.