我正在尝试使用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
函数会尝试使用参数p
和q
(制作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
它基本上是相同的代码,为什么它在函数之外工作但不在函数内部?
这个错误信息让我面前的许多程序员感到困惑。我考虑过......
implicit val lucas
- 没有帮助。-Xprint:typer
diagnostics - 不确定该如何处理这些信息。我可能会继续阅读几个小时,但我认为最好在此时寻求帮助。我正在寻找解决方案和解释。 (我熟悉函数式编程,但对Scala不熟悉,所以如果解释涉及“合成”和“隐式”等术语,那么我可能还需要对其进行额外的解释。)
答案 0 :(得分:5)
这里有一个答案,但由于某种原因它被删除了。
基本上有两种选择。您可以将val
变为lazy val
。或者您可以将类中的lucas: Stream[Int]
定义为字段。您可以在构造函数中使用p
和q
来参数化类。
你是对的,原始代码是懒惰的。但scala翻译它并不够懒惰。
为了简单起见,请考虑将要翻译的代码val a = 1 + a
(我知道代码没有多大意义)。在Java int a = 1 + a
将无法正常工作。 Java将尝试在a
中使用1 + a
,但a
尚未初始化。即使Java有Integer a = 1 + a
,a
也是引用,Java仍然无法执行此操作,因为Java在分配1 + a
a
语句
所以它给我们留下了两个选择。将a
定义为不是变量,而是定义为字段。 Scala通过定义递归方法而不是字段来自动解决问题 - 因为scala中的字段无论如何都是两个方法+变量。或者你可以通过将val指定为lazy val
来明确告诉scala它应该解决这里的延迟问题。这将使scala生成一个隐藏的类,其中包含所有必需的基础结构,使其变得懒惰。
您可以通过使用-print
选项运行编译器来检查此行为。但输出相当复杂,尤其是在lazy val
情况下。
另请注意,由于您的信息流离开了范围,而且您的信息流有两个参数 - p
和q
,如果您选择{{1},您的信息流将在每次调用时重新计算}} 选项。如果您选择创建其他类 - 您可以通过为每个lazy val
和p
可能的
P.S。在这里说q
我当然是指JVM。它更容易用Java来思考