- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在阅读《Joy of Kotlin》一书时遇到了这个有趣的问题。在第4章中,在解释尾递归时,作者提供了一个将两个数字相加的实现,如下所示。
tailrec fun add(a: Int, b: Int): Int = if (b == 0) a else add(inc(a), dec(b))
# where
fun inc(n: Int) = n + 1
fun dec(n: Int) = n - 1
此函数的有趣之处在于 add(3, -3)
返回 0,但如果删除关键字 tailrec
,则会陷入 stackoverflow。当程序看起来不完整时,它如何返回正确的答案。
我反编译了java字节码,看看尾部调用消除是如何完成的,这就是我所看到的。
public static final int add(int a, int b) {
while(b != 0) {
a = inc(a);
b = dec(b);
}
return a;
}
如果我在心里演练代码,循环或之前的递归调用应该会导致无限循环,因为变量 b 永远不会变为零,因为起始值本身是负数。但是,运行上述 kotlin 代码或 java 代码会提供正确的结果。当使用调试器运行时,相同的代码会进入无限循环,正如我在心理演练中所期望的那样。这段代码如何在运行时给出正确的结果,但在 Debug模式下陷入无限循环?
我个人认为正确的实现应该如下,但我无法推理为什么第一个是正确的。
tailrec fun add(a: Int, b: Int): Int =
if (b == 0) a
else if (b > 0) add(inc(a), dec(b))
else add(dec(a), inc(b))
编辑:
@broot 的答案是正确的。使用以下代码验证
tailrec fun add(a: Long, b: Int): Long =
when {
(b == 0) -> a
(b > 0) -> add(inc(a), dec(b))
else -> add(dec(a), inc(b))
}
fun inc(n: Long) = n + 1
fun dec(n: Long): Long = n - 1
fun inc(n: Int) = n + 1
fun dec(n: Int) = n - 1
fun main() {
println(add(3, -4))
println(add(4, -3))
}
最佳答案
b
从 Int.MIN_VALUE
溢出到 Int.MAX_VALUE
,然后转到 0
。同时a
的方向相反。因为我们需要 2^32 - 3
迭代才能将 b
从 -3
变为 0
,a
正确地减少了 3
。另外,由于迭代次数如此之多,如果没有 tailrec
,它就无法工作。
我们可以通过将 a
一侧更改为使用长整型来轻松验证这一点:
tailrec fun add(a: Long, b: Int): Long = if (b == 0) a else add(inc(a), dec(b))
fun inc(n: Long) = n + 1
fun dec(n: Int) = n - 1
在本例中,结果为 4294967296
。
即使它正确地计算了该值,这样使用它也可能不是一个好主意。这几乎是偶然地正常工作。
关于java - 为什么添加 tailrec 会使不正确的 kotlin corecursion 工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76940035/
我已经使用并阅读了 @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 >
我是一名优秀的程序员,十分优秀!