如Eric Lippert的博客文章Closing over the loop variable considered harmful中所述,關閉C#中的循環變量可能會產生意想不到的後果。我試圖瞭解是否將相同的「陷阱」應用於Scala。關閉Scala中的循環變量
首先,因爲這是一個Scala的問題,我會盡力解釋埃裏克利珀的C#示例增加了他的代碼
// Create a list of integers
var values = new List<int>() { 100, 110, 120 };
// Create a mutable, empty list of functions that take no input and return an int
var funcs = new List<Func<int>>();
// For each integer in the list of integers we're trying
// to add a function to the list of functions
// that takes no input and returns that integer
// (actually that's not what we're doing and there's the gotcha).
foreach(var v in values)
funcs.Add(()=>v);
// Apply the functions in the list and print the returned integers.
foreach(var f in funcs)
Console.WriteLine(f());
大多數人想到這個程序打印100,110,120幾點意見。它實際上打印120,120,120。 問題是我們添加到funcs
列表() => v
函數關閉的變量變量,而不是v的值。當v改變值時,在第一個循環中,我們添加到funcs
列表中的所有三個閉包「看到」相同的變量v,(當我們在第二個循環中應用它們時)對於它們全部具有值120。
我試着翻譯示例代碼來斯卡拉:
import collection.mutable.Buffer
val values = List(100, 110, 120)
val funcs = Buffer[() => Int]()
for(v <- values) funcs += (() => v)
funcs foreach (f => println(f()))
// prints 100 110 120
// so Scala can close on the loop variable with no issue, or can it?
斯卡拉是否確實不是來自同一個問題遭受或曾經我只是翻譯埃裏克利珀的代碼不好,未能重現呢?
這種行爲已經絆倒了很多勇敢的C#開發人員,所以我想確保沒有奇怪的與Scala類似的陷阱。但是,一旦你明白了爲什麼C#的行爲如此,那麼Eric Lippert的示例代碼類型的輸出就是有意義的(基本上這就是閉包的工作方式):那麼Scala的行爲有什麼不同呢?
'v'不是Scala代碼中的可變變量。請記住,'for'的理解是''不''循環。實際上,Scala代碼本質上比標準的'for'循環更具功能性,因此,在C#代碼中有一個'v'的值很多的情況下,你有多個'v's,每個''都有自己的單值在Scala代碼中。 – Destin 2012-03-23 16:15:47
@Destin:謝謝,你應該發佈這個答案。我至少會投票贊成。 (你仍然可以這樣做,實際上) – 2012-03-23 16:38:51