Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 无法优化方法_Scala_Inline_Tail Recursion - Fatal编程技术网

Scala 无法优化方法

Scala 无法优化方法,scala,inline,tail-recursion,Scala,Inline,Tail Recursion,下面是引发编译错误“递归调用不在尾部位置”的最小代码。但是,我使用的是@inline,递归调用处于尾部位置。我之所以使用此@inline是因为我将原始reccall的代码复制了两次 import scala.annotation._ object Test { @tailrec private def test(i: Int): Int = { @inline def reccall(i: Int): Int = test(i-1) i match { case

下面是引发编译错误“递归调用不在尾部位置”的最小代码。但是,我使用的是
@inline
,递归调用处于尾部位置。我之所以使用此
@inline
是因为我将原始
reccall
的代码复制了两次

import scala.annotation._
object Test {
  @tailrec private def test(i: Int): Int = {
    @inline def reccall(i: Int): Int = test(i-1)
    i match {
      case 0 => 0
      case i => reccall(i)
    }
  }
}

我已经看过了答案,但它们不适用于我的情况。很好地使用Scala 2.12,JVM中如何实现尾部递归的机制如下所示:

在尾部递归的情况下,Scala可以消除 新建堆栈帧,只需重新使用当前堆栈帧。堆栈 无论递归调用执行了多少次,都不会再深入 制造

因此,在您的情况下,它不能重用属于
test
方法的当前堆栈帧,因为它必须为
reccall
方法创建新的堆栈帧

在这种情况下,递归调用是隐式的,由另一个方法进行。所以我相信你不可能在这种情况下实现尾部递归

您可以完全删除
reccall
方法,然后编写
casei=>test(i-1)
,这样编译器就不会抱怨了


注意:我还认为
@inline
与此无关,在本例中也不是必需的,因为如果我删除它,编译器仍然会抱怨相同的原因。

那么,在JVM中如何实现尾部递归的机制将以以下方式解释:

在尾部递归的情况下,Scala可以消除 新建堆栈帧,只需重新使用当前堆栈帧。堆栈 无论递归调用执行了多少次,都不会再深入 制造

因此,在您的情况下,它不能重用属于
test
方法的当前堆栈帧,因为它必须为
reccall
方法创建新的堆栈帧

在这种情况下,递归调用是隐式的,由另一个方法进行。所以我相信你不可能在这种情况下实现尾部递归

您可以完全删除
reccall
方法,然后编写
casei=>test(i-1)
,这样编译器就不会抱怨了


注意:另外,我认为
@inline
与此无关,在本例中也不是必需的,因为如果我删除它,编译器仍然会抱怨相同的原因。

这里的问题是
@inline
只是一个严格的建议:它不能保证编译器将内联函数。由于
@tailrec
仅在绝对保证可以消除尾部调用的情况下才起作用,这意味着使用
@tailrec
必须假定没有内联。

这里的问题是
@inline
严格地说是建议性的:它不保证编译器将内联函数。由于
@tailrec
只有在绝对保证可以消除尾部调用的情况下才起作用,这意味着使用
@tailrec
必须假设没有内联。

看起来,
@inline
的实现方式是它仍然通过堆栈传递参数。通过内联插入代码消除了跳转,但堆栈仍然用于参数。这使得它不可能处于尾部位置,因为调用完成后需要清理堆栈


此外,用
@inline
注释函数并不能保证优化器会将其内联,只是它会“特别努力”

看来,
@inline
的实现方式仍然是通过堆栈传递参数。通过内联插入代码消除了跳转,但堆栈仍然用于参数。这使得它不可能处于尾部位置,因为调用完成后需要清理堆栈


此外,用
@inline
注释函数并不能保证优化器会将其内联,只是它会“特别努力”

我非常喜欢你的答案。它强调了提问者希望
@inline
能够消除额外的堆栈框架。您是否有任何参考资料证明@inline实际上仍在将参数推入堆栈,而不是像我预期的那样推入局部变量?是的,我不是:)我只是猜测,它可能会这样做。我非常喜欢您的答案。它强调了提问者希望
@inline
能够消除额外的堆栈框架。您是否有任何参考资料证明@inline实际上仍在将参数推入堆栈,而不是像我预期的那样推入局部变量?是,我不是:)我只是猜测,对
reccall
的调用不应该手动内联,因为在我的实际示例中,它考虑了很多代码。请理解,这是一个简化的代码,不是我的完整代码。@MikaëlMayer,我现在明白了。我最初并不认为
@inline
将消除堆栈帧,并通过局部变量推送参数。现在我明白了。对
reccall
的调用不应该手动内联,因为在我的实际示例中它考虑了很多代码。请理解,这是一个简化的代码,不是我的完整代码。@MikaëlMayer,我现在明白了。我最初并不认为
@inline
将消除堆栈帧,并通过局部变量推送参数。现在我明白了。