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

时间:2016-01-31 08:37:27

标签: scala compiler-errors lazy-sequences forward-reference

我正在尝试使用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
  }
}

fibo基于Fibonacci sequence example in Scala's Stream documentation,并且有效。

但是,firstKind函数会尝试使用参数pq(制作Lucas sequences of the first kind)来推广序列,但会出现以下错误:

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

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

这个错误信息让我面前的许多程序员感到困惑。我考虑过......

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

1 个答案:

答案 0 :(得分:5)

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

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

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

为了简单起见,请考虑将要翻译的代码val a = 1 + a(我知道代码没有多大意义)。在Java int a = 1 + a将无法正常工作。 Java将尝试在a中使用1 + a,但a尚未初始化。即使Java有Integer a = 1 + aa也是引用,Java仍然无法执行此操作,因为Java在分配1 + a

时会运行a语句

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

您可以通过使用-print选项运行编译器来检查此行为。但输出相当复杂,尤其是在lazy val情况下。

另请注意,由于您的信息流离开了范围,而且您的信息流有两个参数 - pq,如果您选择{{1},您的信息流将在每次调用时重新计算}} 选项。如果您选择创建其他类 - 您可以通过为每个lazy valp可能的

缓存此类的所有实例来控制此类

P.S。在这里说q我当然是指JVM。它更容易用Java来思考