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

时间:2014-03-24 16:26:21

标签: scala

  

声明或定义引入的名称范围是   包含绑定的整个语句序列。但是,有一个   块中前向引用的限制:在语句序列中   如果s[1]...s[n]中的简单名称引用,则s[i]构成一个块   由s[j] j >= i定义的实体,然后是所有s[k]   介于s[i]s[j]之间,包括<{1}},

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

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

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

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

2 个答案:

答案 0 :(得分:6)

通常你不能拥有这个:

val e: Int = 2
val a: Int = b+c
val b: Int = c
val c: Int = 1
val d: Int = 0

因为在定义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

这实际上将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

其中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
     }
  }
}

修改

val在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)
    }
  }

答案 1 :(得分:2)

您试图避免引用可证明未初始化(或可能未初始化)的实体。

在一个块中,赋值按源顺序发生,但在类模板中,可以提前覆盖和初始化成员。

例如,

{ val a = b ; val b = 1 }  // if allowed, value of a is undefined

但在模板中

class X { val a = b ; val b = 1 }       // warning only
val x = new { override val b = 2 } with X
x.a  // this is 2
class Y(override val b: Int) extends X  // similarly

您还想避免这种情况:

locally {
  def a = c
  val b = 2     // everything in-between must be lazy, too
  def c = b + 1 
}

本地对象与lazy vals明确相同:

{ object p { val x = o.y } ; object o { val y = 1 } }

其他类型的前向参考:

{ val x: X = 3 ; type X = Int }

该规范讨论了对&#34;实体&#34;的前向引用。 - &#34;名称是指实体&#34; - 其他地方意味着术语和类型,但显然它实际上只意味着这里的术语。

它会让你伤害自己:

{ def a: Int = b ; def b: Int = a; a }

也许你的自我毁灭模式必须明确定义。那好吧。