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]
时会抛出一个越界错误
答案 0 :(得分:3)
是的,这是预期的行为(编辑:......它实际上是一个古老的已知错误!https://issues.dlang.org/show_bug.cgi?id=2043所以只能预料到它会发生并且你可以很容易地习惯它,但它实际上并非如此。 ; t应该发生)并且在其他语言中也可以看到,所以这是一个很好的原则。
要在循环中获取变量的单独副本,请调用另一个返回要存储的委托的函数,并将循环变量传递给它。
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
关键字编写一个简写的东西。但是function
并没有捕获变量,delegate
会这样做..)
然后,最后,它周围的括号和最后的(a)
只是调用函数。里面的东西与makeHandler
相同,(...)(a)
称之为;它是makeHadndler(a)
。