为什么在 Scala 中使用下划线时只定义一次值

  • 本文关键字:定义 一次 下划线 Scala scala
  • 更新时间 :
  • 英文 :


让我们看几个例子。

scala> import scala.util.Random
import scala.util.Random
scala> :paste
// Entering paste mode (ctrl-D to finish)
Seq(1, 2, 3).map {
  val rand = new Random().nextInt
  _ + rand
}
// Exiting paste mode, now interpreting.
res0: Seq[Int] = List(-921709014, -921709013, -921709012)
scala> :paste
// Entering paste mode (ctrl-D to finish)
Seq(1, 2, 3).map {
  _ + new Random().nextInt
}
// Exiting paste mode, now interpreting.
res1: Seq[Int] = List(-884268781, 516035717, -2054776549)
scala> :paste
// Entering paste mode (ctrl-D to finish)
Seq(1, 2, 3).map { i =>
  val rand = new Random().nextInt
  i + rand
}
// Exiting paste mode, now interpreting.
res2: Seq[Int] = List(-1258337635, 1817183115, -1994392)

在这里,res0的值被添加到同一个随机数中,这表明相同的随机数生成了三次(极不可能),或者只生成了一个随机数。 res1看起来符合预期,但它告诉我们直接函数调用,而无需按预期分配给val生成的随机数。最后,res2看起来也正确,唯一的区别是使用了变量而不是使用_

我很难理解为什么res0res2的行为不同。特别是,为什么res0没有像人们期望的那样表现。

第一个:

Seq(1, 2, 3).map {
  val rand = new Random().nextInt
  _ + rand
}

相当于:

// nextInt is outside the body of the function given to map
Seq(1, 2, 3).map({
  val rand = new Random().nextInt // nextInt evaluated once here (before map)
  i => i + rand                   // only this function given to map
})

第二种:

Seq(1, 2, 3).map {
  _ + new Random().nextInt
}

相当于:

// nextInt is inside the body given to map
Seq(1, 2, 3).map {
  i => i + new Random().nextInt 
}

给定您的代码:

Seq(1, 2, 3).map {
  val rand = new Random().nextInt
  _ + rand
}

这可以重写为:

Seq(1, 2, 3).map({
  val rand = new Random().nextInt
  x => x + rand
})

在这里你可以看到,rand值不是在实际的函数定义x => x + rand中计算的。它从定义函数的上下文中关闭。所以基本上它是一个结束。因此,每当计算函数x => x + rand时,都会重用先前计算的rand值。

有关更多详细信息,请查看 Programming in Scala 一书中的函数和闭包(8.7 闭包)一章。

最新更新