【问题标题】:Why can't i define a variable recursively in a code block?为什么我不能在代码块中递归地定义变量?
【发布时间】:2014-01-17 14:06:34
【问题描述】:

为什么我不能在代码块中递归定义变量?

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 关键字解决了这个问题,但我不明白为什么它在没有代码块的情况下工作,但在代码块中抛出编译错误。

【问题讨论】:

    标签: scala recursion


    【解决方案1】:

    请注意,在 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
    

    这不适用于块内的vals。在一个块中,所有内容都必须按线性顺序定义。因此vals 不再是成员,而是普通变量(或值,分别)。以下也不编译:

    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
                 ^
    

    这不是递归造成的差异。区别在于类和对象允许前向引用,而块则不允许。

    【讨论】:

      【解决方案2】:

      我会在你写的时候补充一下:

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

      你实际上是在写这个:

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

      当你在定义中声明这些东西时,那个小this 就不见了。

      【讨论】:

        【解决方案3】:

        这种行为的原因取决于不同的 val 初始化时间。如果直接在 REPL 中键入 val x = 5x 将成为对象的成员,其值可以使用默认值(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 描述了不同的初始化时间。

        【讨论】:

        • 最后一个例子没有编译。 (这当然是问题的全部。)
        • @Debilski:你说得对,2.10 不再编译。我使用 2.9.0 来编译它,如错误报告中所述。
        • 我使用的是 2.9.1-1。所以它一定是在两者之间改变了。
        【解决方案4】:

        我想补充一点,基于 Eclipse 的 Scala-IDE (v4.0.0) 中的 Scala 工作表的行为与预期的 REPL 不同(例如,https://github.com/scala-ide/scala-worksheet/wiki/Getting-Started 说“工作表就像一个 REPL 会话类固醇”)在这方面,但更像是一个长方法的定义:也就是说,工作表中的前向引用 val 定义(包括递归 val 定义)必须成为某个对象或类的成员。

        【讨论】:

          猜你喜欢
          • 2017-01-16
          • 2019-04-23
          • 1970-01-01
          • 2022-06-28
          • 1970-01-01
          • 2019-09-14
          • 2021-01-14
          • 1970-01-01
          相关资源
          最近更新 更多