Scala错误:当代码出现在函数中时,"前向引用扩展了值的定义"

200*_*ess 0 scala compiler-errors forward-reference lazy-sequences

我正在尝试使用Scala 2.11.7编译以下代码.

object LucasSeq {
  val fibo: Stream[Int] = 0 #:: 1 #:: fibo.zip(fibo.tail).map { pair =>
    pair._1 + pair._2
  }

  def firstKind(p: Int, q: Int): Stream[Int] = {
    val lucas: Stream[Int] = 0 #:: 1 #:: lucas.zip(lucas.tail).map { pair =>
      p * pair._2 - q * pair._1
    }
    lucas
  }
}
Run Code Online (Sandbox Code Playgroud)

fibo基于Scala Stream文档中Fibonacci序列示例,它可以工作.

但是,firstKind试图用参数推广序列pq(制作第一类Lucas序列)的函数有以下错误:

LucasSeq.scala:7: error: forward reference extends over definition of value lucas
    val lucas: Stream[Int] = 0 #:: 1 #:: lucas.zip(lucas.tail).map { pair =>
                                         ^
one error found
Run Code Online (Sandbox Code Playgroud)

它基本上是相同的代码,为什么它在函数外部工作但不在函数内部?


这个错误信息困扰了我之前的许多程序员.我考虑过......

我可能会继续阅读几个小时,但我认为最好在此时寻求帮助.我正在寻找解决方案和解释.(我熟悉函数式编程,但对Scala不熟悉,所以如果解释涉及"合成"和"隐式"等术语,那么我可能还需要对其进行额外的解释.)

Arc*_*heg 5

这里有一个答案,但由于某种原因它被删除了.

基本上有两种选择.你可以让你val进入lazy val.或者您可以将lucas: Stream[Int]类定义为字段.您可以使用构造函数pq在构造函数中参数化类.

你是对的,原始代码是懒惰的.但scala翻译它并不够懒惰.

为了简单起见,请考虑val a = 1 + a将要翻译的代码(我知道代码没有多大意义).在Java int a = 1 + a中将无法正常工作.Java将尝试使用a1 + a,但a还没有被初始化.即使Java已经Integer a = 1 + a并且a将成为引用,Java仍然无法执行此操作,因为Java 1 + a在分配时运行语句a

所以它给我们留下了两个选择.a不是作为变量定义,而是作为字段定义.Scala通过定义递归方法而不是字段来自动解决问题 - 因为scala中的字段无论如何都是两个方法+变量.或者你可以通过指定你的val来明确地告诉scala它应该解决这里的延迟问题lazy val.这将使scala生成一个隐藏类,其中包含所有必需的基础结构,使其变得懒惰.

您可以通过使用-print选项运行编译器来检查此行为.但输出相当复杂,尤其是lazy val以防万一.

另请注意,因为您的信息流离开了范围,而且您的信息流还有两个参数 - p而且q,如果您选择使用,则每次调用都会重新计算您的信息流lazy val.如果您选择创建一个额外的类 - 您可以通过为每个类pq可能的高速缓存此类的所有实例来控制它

PS在Java这里说我当然是指JVM.从Java的角度来看,它更容易思考