Scala错误:“前向引用扩展了值的定义";当代码出现在函数中时 [英] Scala error: "forward reference extends over definition of value" when code appears in a function
问题描述
我正在尝试使用Scala 2.11.7编译以下代码.
I'm trying to compile the following code, using 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 Scala的Stream
文档中的序列示例,它可以工作.
fibo
is based on the Fibonacci sequence example in Scala's Stream
documentation, and it works.
However, the firstKind
function, which tries to generalize the sequence with parameters p
and q
(making Lucas sequences of the first kind), has the following error:
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
基本上是相同的代码,那么为什么它在函数外部而不在函数内部起作用?
It's basically the same code, so why does it work outside the function but not inside a function?
此错误消息使我之前的许多程序员感到困惑.我考虑过...
This error message has puzzled many programmers before me. I've considered…
- 因此,不要将代码放在函数中-但我要做想要一个函数.
-
implicit val lucas
—没有帮助. - 自引用只能在惰性表达式中使用-但这 是惰性的,对吧?
- 使用
-Xprint:typer
诊断程序进行编译-不确定如何处理该信息. - 这是一个影子问题吗? —不,我使用的是不会冲突的标识符.
- 编译器错误?-我希望不会.所引用的错误应已在2.11.7中修复.
- So just don't put that code in a function — but I do want a function.
implicit val lucas
— doesn't help.- Self-references can only be used in lazy expressions — but this is lazy, right?
- Compile with
-Xprint:typer
diagnostics — not sure what to do with that information. - Is it a shadowing issue? — No, I'm using identifiers that don't clash.
- Compiler bug? — I hope not. The referenced bug should be already fixed in 2.11.7.
我可能会继续读书几个小时,但我认为此时最好寻求帮助.我正在寻找解决方案和解释. (我熟悉函数式编程,但对Scala还是陌生的,因此,如果解释涉及诸如合成"和隐式"之类的术语,那么我可能还需要对此进行额外的解释.)
I could probably go on reading for hours, but I think it would be best to ask for help at this point. I'm looking for both a solution and an explanation. (I'm familiar with functional programming, but new to Scala, so if the explanation involves terms like "synthetic" and "implicit", then I'll probably need an additional explanation of that as well.)
推荐答案
这里有一个答案,但是由于某种原因被删除了.
There was an answer here, but it was deleted for some reason.
基本上有两种选择.您可以将val
转换为lazy val
.或者,您可以将类中的lucas: Stream[Int]
定义为字段.您可以在构造函数中使用p
和q
参数化该类.
There are basically two options. You could make your val
into lazy val
. Or you could define your lucas: Stream[Int]
in a class as a field. You can parameterize the class with p
and q
in the constructor.
您是对的,原始代码是惰性的.但是对于scala来说,翻译它还不够懒.
You are right that the original code is lazy. But it is not lazy enough for scala to translate it.
为简单起见,请考虑将什么代码转换为val a = 1 + a
(我知道该代码没有多大意义).在Java中,int a = 1 + a
将不起作用. Java将尝试在1 + a
中使用a
,但是a
尚未初始化.即使Java具有Integer a = 1 + a
,并且a
将作为引用,Java仍然无法执行此操作,因为Java在分配a
For the sake of simplicity think into what code val a = 1 + a
will be translated (I know the code does not make sense much). In Java int a = 1 + a
won't work. Java will try to use a
in 1 + a
, but a
is not yet initialized. Even if Java had Integer a = 1 + a
, and a
would be a reference, Java still not able to execute this, because Java runs 1 + a
statement when allocating a
因此,我们有两个选择. a
的定义不是变量,而是字段. Scala通过定义递归方法而不是字段来自动解决问题-因为scala中的field无论如何是两种方法+变量.或者您可以明确地告诉scala它应该通过将val指定为lazy val
来解决懒惰问题.这将使scala生成具有所有必需基础结构的隐藏类,以使其变得懒惰.
So it leaves us with two options. Defining a
not as a variable, but as a field. Scala automatically resolve the problem by defining a recursive method, instead of a field - because field in scala is two methods + variable anyway. Or you could tell scala explicitly that it should resolve the lazy problem here by specifying your val as lazy val
. This will make scala generate a hidden class with all the necessary infrastructure for it to be lazy.
您可以通过使用-print
选项运行编译器来检查此行为.输出非常复杂,尤其是在lazy val
情况下.
You can check this behavior by running your compiler with -print
option. The output is rather complicated though, especially in lazy val
case.
还请注意,由于流离开了作用域,并且还具有流的两个参数-p
和q
,因此,如果使用lazy val
选项,则会在每次调用时重新计算流.如果选择创建其他类,则可以通过为每个p
和q
可能的缓存该类的所有实例
Also please note that because your stream leaves the scope and also because you have two parameters for your stream - p
and q
, your stream will be recomputed each call if you go with lazy val
option. If you choose creating an additional class - you are able to control this, by caching all instances of this class for each p
and q
possible
P.S.在这里说Java
当然是指JVM.就Java而言更容易思考
P.S. By saying Java
here I of course mean JVM. It just easier to think in terms of Java
这篇关于Scala错误:“前向引用扩展了值的定义";当代码出现在函数中时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!