为什么必须将Scala中块内的引用值转发为惰性?

cor*_*zza 10 scala

声明或定义引入的名称范围是包含绑定的整个语句序列.然而,在块向前引用的限制:在一份声明序列 s[1]...s[n]组成一个块,如果一个简单的名字s[i]指的是一个实体的定义s[j]在那里j >= i,那么对于所有s[k] 之间,包括s[i]s[j],

  • s[k] 不能是变量定义.
  • 如果s[k]是值定义,则必须是lazy.

编辑:我不确定MikaëlMayer的答案是否真的解释了一切.考虑:

object Test {
  def main(args: Array[String]) {
    println(x)
    lazy val x: Int = 6
  }
}
Run Code Online (Sandbox Code Playgroud)

在这里,x必须在代码中实际定义之前读取/评估惰性值!这与Mikaël声称懒惰评估消除了在定义之前评估事物的需要相矛盾.

Mik*_*yer 6

通常你不能这样:

val e: Int = 2
val a: Int = b+c
val b: Int = c
val c: Int = 1
val d: Int = 0
Run Code Online (Sandbox Code Playgroud)

因为在定义a时尚未定义值c.因为引用c,a和c之间的所有值都应该是惰性的,以避免依赖

val e: Int = 2
lazy val a: Int = b+c
lazy val b: Int = c
lazy val c: Int = 1
val d: Int = 0
Run Code Online (Sandbox Code Playgroud)

这实际上将a,b和c转换为在读取时初始化其值的对象,这将在声明之后,即这相当于:

val e: Int = 2
var a: LazyEval[Int] = null
var b: LazyEval[Int] = null
var c: LazyEval[Int] = null
a = new LazyEval[Int] {
  def evalInternal() = b.eval() + c.eval()
}
b = new LazyEval[Int] {
  def evalInternal() = c.eval()
}
c = new LazyEval[Int] {
  def evalInternal() = 1
}
val d = 0
Run Code Online (Sandbox Code Playgroud)

LazyEval将在何处(如下所示)(由编译器本身实现)

class LazyEval[T] {
  var value: T = _
  var computed: Boolean = false
  def evalInternal(): T // Abstract method to be overriden
  def eval(): T = {
     if(computed) value else {
       value = evalInternal()
       computed = true
       value
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

编辑

vals在java中并不存在.它们是局部变量或在计算中不存在.因此,lazy val的声明在任何事情发生之前就存在了.请记住,闭包是在Scala中实现的.您的块将被重写为:

  object Test {
    def main(args: Array[String]) {
      // Declare all variables, val, vars.
      var x: Lazy[Int] = null
      // No more variables to declare. Lazy/or not variable definitions
      x = new LazyEval[Int] {
        def evalInternal() = 6
      }
      // Now the code starts
      println(x)
    }
  }