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]时,它会抛出一个超出界限的错误。
发布于 2015-04-20 23:15:26
是的,这是预期的行为(编辑:...it实际上是一个古老的已知bug!bug.cgi?id=2043,所以只希望它发生,而且您可以很容易地习惯它,但它实际上是不应该发生的),而且在其他语言中也可以看到,所以这是一个很好的了解原则。
若要获得循环中变量的单独副本,请调用返回要存储的委托的另一个函数,将循环变量传递给它。
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);行已经更改:它定义了返回委托的快速委托。额外的函数返回函数关闭在循环状态的快照上,而不仅仅是函数级局部变量。
我在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的原因是一样的,只是语法不同,并且分解成更大的函数,而不是像一行代码那样做。
我们定义了一个函数,它返回一个使用捕获的循环变量的函数。在使用点,我们调用一个函数,该函数返回存储给以后使用的委托。
在简写D语法((b) => { write(b);})(a);中,(b) => ...是javascript中看到的makeHandler函数。它返回的{ write(b); }是JS中return function() { ... }的缩写(BTW --相同的JS语法基本上也适用于D,您可以用delegate或function关键字编写一个长的东西。虽然D的function不捕获变量,但delegate是这样做的)
最后,它周围的括号和最后的(a)只是调用函数。里面的东西和makeHandler是一样的,(...)(a)叫它;它是makeHadndler(a)。
https://stackoverflow.com/questions/29759419
复制相似问题