【发布时间】:2023-03-28 04:30:02
【问题描述】:
正如 Eric Lippert 的博文 Closing over the loop variable considered harmful 中所讨论的,在 C# 中关闭循环变量可能会产生意想不到的后果。我试图了解相同的“陷阱”是否适用于 Scala。
首先,由于这是一个 Scala 问题,我将尝试解释 Eric Lippert 的 C# 示例,在他的代码中添加一些 cmets
// 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 的值。当 v 改变值时,在第一个循环中,我们添加到 funcs 列表中的所有三个闭包“看到”同一个变量 v,(当我们在第二个循环中应用它们时)它们的值都是 120 .
我尝试将示例代码翻译成 Scala:
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?
Scala 是否确实没有遇到同样的问题,或者我只是将 Eric Lippert 的代码翻译得很糟糕并且未能重现它?
这种行为让许多勇敢的 C# 开发人员感到不安,所以我想确保 Scala 没有奇怪的类似陷阱。而且,一旦你理解了 C# 的行为方式,Eric Lippert 的示例代码的输出就会变得有意义(基本上就是闭包的工作方式):那么 Scala 有什么不同呢?
【问题讨论】:
-
v在 Scala 代码中不是可变变量。请记住,for推导式 notfor循环。 Scala 代码实际上转换为本质上比标准for循环更实用的东西,因此,如果您有一个v在 C# 代码中有许多值,那么您有多个vs,每个都有自己的单个值在 Scala 代码中。 -
@Destin:谢谢,您应该将其发布为答案。我至少会赞成它。 (实际上,你仍然可以这样做)