Kotlin的tailrec有什么意义?
Kotlin的tailrec有什么意义?,kotlin,Kotlin,tailrec优化存在尾部递归的函数。为什么编译器不直接优化它呢 C编译器优化尾部递归。您不必将该方法标记为具有尾部递归。编译器只是注意到最后一个操作是递归的。就这样 为什么这个看似过多的关键字会存在?我错过什么了吗?这纯粹是为了方便编译器,而不是为了方便用户吗?关键字告诉编译器函数的实现必须是尾部递归的,如果函数实际上不是尾部递归的,则会导致编译器报告错误。当对函数实现的更改导致函数不再是尾部递归,并导致性能意外下降(或由于堆栈溢出错误导致生产完全失败)时,它可以保护用户.我将继续并猜测这是为
tailrec
优化存在尾部递归的函数。为什么编译器不直接优化它呢
C编译器优化尾部递归。您不必将该方法标记为具有尾部递归。编译器只是注意到最后一个操作是递归的。就这样
为什么这个看似过多的关键字会存在?我错过什么了吗?这纯粹是为了方便编译器,而不是为了方便用户吗?关键字告诉编译器函数的实现必须是尾部递归的,如果函数实际上不是尾部递归的,则会导致编译器报告错误。当对函数实现的更改导致函数不再是尾部递归,并导致性能意外下降(或由于堆栈溢出错误导致生产完全失败)时,它可以保护用户.我将继续并猜测这是为了能够更仔细地编写尾部递归函数。通过显式地要求关键字,您将知道编译器优化肯定会发生(您不会猜测编译器是否成功优化了您的函数,或者您是否会在运行时出现堆栈溢出),另外,如果您打破了使用
tailrec
生成的函数的尾部递归规则,您的代码甚至不会编译,如文档所述:
要符合tailrec修饰符的条件,函数必须在执行最后一个操作时调用自身
回想一下Kotlin的官方文件只是说: 当一个函数被标记为tailrec修饰符并满足所需的形式时,编译器会优化递归,留下一个快速高效的基于循环的版本
这强烈表明,如果tailrec关键字不存在,则不保证进行循环转换。基于@szmb13@yole answers的代码示例 尝试使用n=10000运行此代码,如果出现错误,请尝试添加tailrec关键字并再次运行
fun fibonacci(n: Int, a: BigInteger, b: BigInteger): BigInteger {
return if (n == 0) b
else fibonacci(n - 1, a + b, a)
}
您可以看到不同的,那么这是否意味着即使符合条件,也不会针对尾部递归优化未标记tailrec的函数?这确实给人留下了这样的印象(至少对我是这样)。这让我想知道它是否真的只是给出编译器提示。这部分是b/c,JVM本身目前无法针对尾部递归进行优化(请参阅)。JVM上的许多语言,例如Clojure和Kotlin,都公开了一个表单(在Clojure中重复出现)或一个关键字(在Kotlin中tailrec),以便在语言级别上捕捉到这一点。@HelloWorld,我知道Clojure是这样,Kotlin也是这样。根据文档,Kotlin编译器需要关键字
tailrec
来进行优化。编译器不需要提示来检测尾部递归函数;这是一张相当微不足道的支票。我已经解释了这个关键字存在的原因。如果你不相信你给出的答案,问问题有什么意义?@yole-Yours和zsmb13似乎相当,他只比我快了一盎司,所以我把他的答案标记为正确。然而,阅读tankthinks reply让我重新思考。如果这是真的,为什么你不能有一个既能打开
又能tailrec
的方法呢?我也对这个限制很好奇,并了解到它最初是受支持的,但后来由于编译问题被阻止:@yole似乎应该有比禁用open
成员上的tailrec
修饰符更好的解决方案。如果最终能够取消这一限制,那将是一件好事。