为什么我不能在代码块中递归地定义变量?
scala> {
| val test: Stream[Int] = 1 #:: test
| }
<console>:9: error: forward reference extends over definition of value test
val test: Stream[Int] = 1 #:: test
^
scala> val test: Stream[Int] = 1 #:: test
test: Stream[Int] = Stream(1, ?)
lazy
关键字解决了这个问题,但我无法理解为什么它在没有代码块的情况下工作但在代码块中抛出了编译错误。
答案 0 :(得分:23)
请注意,在REPL中
scala> val something = "a value"
的评估或多或少如下:
object REPL$1 {
val something = "a value"
}
import REPL$1._
因此,任何val
(或def
等)都是内部REPL帮助程序对象的成员。
现在重点是类(和对象)允许对其成员进行前向引用:
object ForwardTest {
def x = y // val x would also compile but with a more confusing result
val y = 2
}
ForwardTest.x == 2
块内的val
不是这样。在一个块中,所有内容都必须按线性顺序定义。因此val
不再是成员,而是普通变量(或值,分别为)。以下内容无法编译:
def plainMethod = { // could as well be a simple block
def x = y
val y = 2
x
}
<console>: error: forward reference extends over definition of value y
def x = y
^
这不是递归而产生差异的。不同之处在于类和对象允许前向引用,而块则不允许。
答案 1 :(得分:4)
我写的时候会加上:
object O {
val x = y
val y = 0
}
你实际上是在写这个:
object O {
val x = this.y
val y = 0
}
当你在定义中声明这些东西时,那个小this
就是缺少的东西。
答案 2 :(得分:2)
此行为的原因取决于不同的val初始化时间。如果直接向REPL键入val x = 5
,x
将成为对象的成员,可以使用默认值(null,0,0.0,false)初始化这些值。相反,块中的值无法通过默认值初始化。
这往往会有不同的行为:
scala> class X { val x = y+1; val y = 10 }
defined class X
scala> (new X).x
res17: Int = 1
scala> { val x = y+1; val y = 10; x } // compiles only with 2.9.0
res20: Int = 11
在Scala 2.10中,最后一个示例不再编译。在2.9.0中,编译器重新排序这些值以使其编译。有bug report描述了不同的初始化时间。
答案 3 :(得分:0)
我想补充一点,基于Eclipse的Scala-IDE(v4.0.0)中的Scala工作表的行为与人们预期的REPL不同(例如https://github.com/scala-ide/scala-worksheet/wiki/Getting-Started表示&#34;在这方面,工作表就像类固醇的REPL会话一样,而是像一个长方法的定义:也就是说,工作表中的前向引用val定义(包括递归val定义)必须成为某个对象的成员或者类。