Scala 为什么可以';我不能在代码块中递归地定义一个变量吗?

Scala 为什么可以';我不能在代码块中递归地定义一个变量吗?,scala,recursion,Scala,Recursion,为什么不能在代码块中递归定义变量 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
     | }
<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, ?)
scala>{
|val测试:流[Int]=1#:::测试
| }
:9:错误:正向引用扩展到值测试的定义上
val测试:流[Int]=1#:::测试
^
scala>val测试:流[Int]=1#:::测试
测试:流[Int]=流(1,?)

lazy
关键字解决了这个问题,但我不理解为什么它在没有代码块的情况下工作,但在代码块中抛出编译错误。

此行为的原因取决于不同的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中,编译器对值重新排序,以使其进行编译。有一个描述不同初始化时间的函数。

请注意,在REPL

scala> val something = "a value"
评估结果大致如下所示:

object REPL$1 {
  val something = "a value"
}
import REPL$1._
因此,任何
val
(或
def
等)都是内部REPL helper对象的成员

现在关键是类(和对象)允许对其成员进行正向引用:

object ForwardTest {
  def x = y // val x would also compile but with a more confusing result
  val y = 2
}
ForwardTest.x == 2
对于块内的
val
s,情况并非如此。在块中,所有内容都必须按线性顺序定义。因此,
val
s不再是成员,而是普通变量(或值)。以下内容也不会编译:

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
             ^
def plainMethod={//也可以是一个简单的块
def x=y
val y=2
x
}
:错误:正向引用扩展到值y的定义上
def x=y
^

造成差异的不是递归。不同之处在于类和对象允许向前引用,而块不允许向前引用。

我将在编写时添加这一点:

object O {
  val x = y
  val y = 0
}
你实际上写的是:

object O {
  val x = this.y
  val y = 0
}

当你在一个定义中声明这个东西时,这个就是你所缺少的。

我想补充一点,基于Eclipse的Scala IDE(v4.0.0)中的Scala工作表在这方面的行为与人们可能期望的REPL不同(例如,说“工作表就像类固醇上的REPL会话”),但与一个长方法的定义类似:即,工作表中的前向引用val定义(包括递归val定义)必须成为某个对象或类的成员。

最后一个示例不编译。(这当然是问题的全部。)@Debilski:你说得对,2.10版本不再编译了。我使用了2.9.0来编译它,正如bug报告中提到的。我使用的是2.9.1-1。所以它一定是在两者之间被改变了。