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
将消除堆栈帧,并通过局部变量推送参数。现在我明白了。