2

我正在尝试使用 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基于ScalaStream文档中的 Fibonacci 序列示例,并且可以正常工作。

然而,firstKind试图用参数pq(使卢卡斯序列成为第一类)泛化序列的函数有以下错误:

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

基本上是一样的代码,那为什么它在函数外工作,而在函数内却不行呢?


这个错误信息让我之前的许多程序员感到困惑。我考虑过……</p>

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

4

1 回答 1

6

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

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

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

为简单起见,请考虑val a = 1 + a将翻译哪些代码(我知道代码没有多大意义)。在Javaint a = 1 + a中不起作用。Java 将尝试使用ain 1 + a,但a尚未初始化。即使 Java 有Integer a = 1 + a,并且a将是一个引用,Java 仍然无法执行它,因为 Java1 + a在分配时运行语句a

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

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

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

PSJava这里说的我当然是指JVM。用 Java 来思考更容易

于 2016-01-31T10:39:46.783 回答