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

时间:2017-01-12 22:01:41

标签: 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没有像预期的那样表现出来。

2 个答案:

答案 0 :(得分:3)

第一个:

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 
}

答案 1 :(得分:3)

鉴于你的代码:

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值。

有关详细信息,请查看 Scala中的编程一书中的Functions and Closures(8.7个闭包)一章。