- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在 Kotlin 中练习递归,并在运行以下 2 个函数时得到了其他结果:
tailrec fun fac1(n: Long): BigInteger {
if(n == 1L)
return BigInteger.ONE
else
return n.toBigInteger() * fac1(n-1)
}
println("fac1(10000) = ${fac1(10000)}")
fac1(10000)的结果:线程“main”中出现异常java.lang.StackOverflowError
tailrec fun fac2(n: Long, a: BigInteger = BigInteger.ONE): BigInteger {
return if(n == 1L) a else fac2(n -1, a * n.toBigInteger())
}
println("fac2(10000) = ${fac2(10000)}")
fac2(10000) 可以执行并返回结果:fac2(10000) = 284625968091705451890.....
有人可以向我解释一下这两个函数之间的区别以及为什么函数 2 可以执行而函数 1 不能执行吗?
最佳答案
这些函数依赖于一个名为 tail recursion 的概念。 ,由 tailrec
修饰符发出信号。尾递归允许递归函数无限期地调用自身,而不会填满堆栈。它依赖于这样一个事实:递归调用(函数调用自身的位)是方法中的最后一次调用。有关其工作方式和原因的更多信息,请考虑阅读 Wikipedia entry on tail calls 中的一些内容。以下是一小段摘录:
When a function is called, the computer must "remember" the place it was called from [...] so that it can return to that location [...] once the call is complete. Typically, this information is saved on the call stack [...]. For tail calls, there is no need to remember the caller – instead, tail call elimination makes only the minimum necessary changes to the stack frame before passing it on, and the tail-called function will return directly to the original caller.
您的第一个函数的问题在于它并不是真正的尾递归。对于尾递归的函数来说,调用自身必须是它所做的最后一件事。
但是要执行n.toBigInteger() * fac1(n-1)
行,您首先必须执行fac1(n-1)
,并且然后将其乘以n.toBigInteger()
。这意味着该函数执行的最后一件事是乘法,而不是调用 fac1
。
当您编译此代码时,编译器应该警告您 tailrec
修饰符在这里无效。我的编译器给了我这个警告:
A function is marked as tail-recursive but no tail calls are found
由于该函数不是尾递归的,因此每次递归调用自身时,它都必须分配一个新的堆栈帧。调用堆栈的大小有限,最终会变满,从而导致 StackOverflowError
。
第二个函数通过重写函数来解决这个问题,以便对 fac2 的调用实际上是方法中的最后一次调用。这使得它能够正确使用尾递归,这意味着堆栈不会填满。
关于kotlin - kotlin 中这些 tailrec 函数有什么不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68716946/
我已经使用并阅读了 @tailrec注释具有尾递归方法。我已经浏览了许多解释它的链接。例如,它仅适用于自调用函数,不应被覆盖等。 到处都提到compiler optimizes .但是编译器做了什么魔
我有一个名为 ImmutableEntity 的 Java 抽象类以及几个包含名为 @DBTable 的类级注释的子类.我正在尝试使用尾递归 Scala 方法搜索注释的类层次结构: def get
我正在学习如何使用 CompletableFuture 使协程与 Java 库一起工作。下面是我的代码: // x invokes y invokes z invokes Java client su
在下面的代码中,我看到一个警告no tail calls found,但是当作为扩展函数编写时,同一函数没有该警告。现在我很困惑我的IDE是错误的,还是我的Extension方法实际上不是尾部递归的,
这个问题在这里已经有了答案: What is the Scala annotation to ensure a tail recursive function is optimized? (3 个回答
我正在 Kotlin 中练习递归,并在运行以下 2 个函数时得到了其他结果: tailrec fun fac1(n: Long): BigInteger { if(n == 1L)
tailrec 优化存在尾递归的函数。为什么编译器不直接优化它? C 编译器针对尾递归进行了优化。您不必将该方法标记为具有尾递归。编译器只是注意到最后一个操作是递归的。就是这样。 为什么会存在这个看似
在以下 Scala 函数示例中: @tailrec def someFunction( ... ): Unit = { 是@tailrec注释做任何有用的事情还是很高兴知道这是一个尾递归? 最佳答案
在下面,maybeNext.map{rec}.getOrElse(n) 行使用 Option monad 来实现递归或转义模式。 scala> @tailrec
应用@tailrec 我从scala 编译器中得到错误:“无法优化@tailrec 注释方法get:它包含一个递归调用,目标是父类(super class)型case _ => tail.get(n-
在 scala 中,以下 2 个函数的作用完全相同: @tailrec final def fn(str: String): Option[String] = { Option(str).filt
我正在研究 scala TCO 并编写了以下代码 import scala.annotation.tailrec final def tailReccursionEx(str:String):List
我在阅读《Joy of Kotlin》一书时遇到了这个有趣的问题。在第4章中,在解释尾递归时,作者提供了一个将两个数字相加的实现,如下所示。 tailrec fun add(a: Int, b: In
我尝试使用本教程 youtube tutorial .我有一个功能如下: fun fact(x:Int):Int{ tailrec fun factTail(y:Int, z:Int):Int
我正在尝试测试以下 tailrec 函数: private tailrec fun findFixPoint(eps: Double = 5.0, x: Double = 1.0): Doub
斯卡拉 代码: @annotation.tailrec private def fastLoop(n: Int, a: Long = 0, b: Long = 1): Long = if (n >
我是一名优秀的程序员,十分优秀!