【问题标题】:scala closure - force bind to value not referencescala闭包 - 强制绑定到值而不是引用
【发布时间】:2016-08-25 22:19:09
【问题描述】:

我有一个方法可以返回一个简单的函数数组,这些函数只打印它们的订单号:

def buggyClosureFunction(amount: Int) = {
  val functions = new Array[() => Unit](amount);
  var i = 0;
  while (i < amount) {
    functions(i) = {()=>print(i + " ")}
    i += 1;
  }
  functions
}

 val wow = buggyClosureFunction(4);
 wow.foreach(_());

这会打印 4 4 4 4。(所有函数都会打印它们引用的 i 的值。 我似乎无法更改我的方法,以便返回的函数打印 1 2 3 4。

我的一个尝试:

def goodClosureFunction(amount: Int) = {
  val functions: = new Array[() => Unit](amount);
  var i = 0;
  val helper = (num: Int) => {
    print(num);
  }

  while (i < amount) {
    functions(i) = {()=>helper(i)()}
    i += 1;
  }
  functions
}

但这仍然会打印 4 4 4 4。

【问题讨论】:

  • 考虑这个def closures(n: Int) = (0 until n) map (i =&gt; () =&gt; print(i + " ")),没有变量,没有意外的关闭问题。

标签: scala closures


【解决方案1】:

当您关闭 var i = 0 时,Scala 编译器会将 iscala.Int 转换为 scala.runtime.IntRef 并将其提升到其编译器生成的类中。这意味着生成的类包含对所述类型的引用。当函数执行时,值实际上是指向最后一个值,而不是保存每次迭代的值。

为了避免这种情况,请在闭包内创建i 的本地副本:

while (i < amount) {
  functions(i) = {
    val j = i
    () => print(j + " ")
  }
  i += 1;
}

正如@Łukasz 指出的那样,如果您想采用函数式方法并避免关闭问题和可变状态:

def nonBuggyNoClosure(n: Int) = (0 until n) map (i => () => print(i + " "))

更多关于Scala闭包的实现细节在How are closures implemented in scala?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-28
    • 2011-03-20
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    • 1970-01-01
    • 2011-02-24
    • 2023-04-04
    相关资源
    最近更新 更多