Scala错误:“前向引用扩展了值的定义";当代码出现在函数中时 [英] Scala error: "forward reference extends over definition of value" when code appears in a function

查看:579
本文介绍了Scala错误:“前向引用扩展了值的定义";当代码出现在函数中时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

但是,firstKind函数尝试使用参数pq泛化序列(使

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…

  • 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]定义为字段.您可以在构造函数中使用pq参数化该类.

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.

还请注意,由于流离开了作用域,并且还具有流的两个参数-pq,因此,如果使用lazy val选项,则会在每次调用时重新计算流.如果选择创建其他类,则可以通过为每个pq可能的缓存该类的所有实例

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆