Java中的尾递归函数仍然会破坏堆栈

2023-12-13

我正在尝试实现尾递归阶乘计算器,但仍然出现堆栈溢出。谁能帮我找出原因吗?

  • 我读过 Java 8 支持 Tail 调用优化,但我想我一定没有正确实现它。
  • 我读到可以使用 lambda 表达式。我不确定我是否完全理解这个概念,但我仍在阅读。
  • 我只是在寻找有关如何使用真正的尾部调用优化、lambda 表达式或其他任何方式的建议。

code:

package factorielRecursiveTerminale;

import java.math.BigInteger;
import java.util.Scanner; 

public class factorielRecursiveTerminale {  
    
    public static BigInteger factoriel(BigInteger n, BigInteger m) {
        if (n.compareTo(BigInteger.ZERO) < 1) return m;             
        return factoriel(n.subtract(BigInteger.ONE), n.multiply(m));
    }                                                               
                                                                
    public static BigInteger fact(int n) {  //convertir l'entree en BigInteger et lancer la recursion
        if(n < 0) {
            return BigInteger.valueOf(-1);
        }
        BigInteger b = BigInteger.valueOf(n);
        return factoriel(b, BigInteger.ONE);
    }

    public static void runBigFact() {                       //gestion des erreurs + boucle d'entree de valeurs. 
        String valeurRecu = "";
        int valeur;
        BigInteger resultat;
        System.out.println("Calcul Factoriel\n");
        while(!valeurRecu.contentEquals("q")){
            System.out.println("Entrer la valeur a calculer (q - quitter) : ");
            Scanner entree = new Scanner(System.in);
            valeurRecu = entree.nextLine();
            if (valeurRecu.contentEquals("q")) entree.close();
            else {
                try {
                    valeur = Integer.parseInt(valeurRecu);
                }catch (NumberFormatException e){  
                    System.out.println("Pas un entier. Essayer encore.\n");
                    continue;
                  } 
                try {
                    resultat = fact(valeur);
                    if(resultat.compareTo(BigInteger.valueOf(-1)) == 0) {
                        System.out.println("Valeur negative. Essayer encore.\n");
                    }
                    else System.out.println("Factoriel " + valeur + " -> " + fact(valeur) + "\n");
                } catch(StackOverflowError e) {
                    System.out.println("Depassement de la pile. Essayer un entier plus petit.\n");
                    continue;
            }
        }
        }
        System.out.println("Au revoir! :)\n");
    }
    
    public static void main(String[] args) {
        runBigFact();
    }
}

我读过 JAVA 8 支持 Tail 调用优化,但我想我一定没有正确实现它。

那你就读错了。或者,您阅读了正确的陈述,但没有正确解释它。

Java,这种语言,不支持尾调用递归。从来没有。它可能永远不会*。

然而,java(VM)有一些功能使其他非 java 语言更容易编译成类文件以在 java 运行时上运行,以支持 TCO。这大概就是您读到的内容。

我只是在寻找有关如何使用真正的尾部调用优化、lambda 表达式或其他任何方式的建议。

用 scala 或类似的语言编写它。

说真的,java怎么没有TCO???

TCO 成本高昂:Java 有一条规则,即当发生错误时,您将获得堆栈跟踪,而堆栈跟踪是一个定义明确的概念,最重要的是,它为每个逻辑调用跟踪 1 个堆栈帧。这cannot如果存在 TCO,则继续。当然,还有一些选择:堆栈上的每个单独帧都可以获得一个“计数器”,以便堆栈跟踪保持较小的内存占用量,同时正确表示“并且该调用序列已重复出现 8190581 次”。语言规范中还有大量关于它如何工作、何时启动和不启动以及这一切意味着什么的文本,并且规范中的任何其他页面都是永远的维护负担 - 这不是“它”的情况绝对优于向 java 添加 TCO,因此当我们开始使用它时,灌篮高手,任何具有该功能的 Pull 请求都将立即集成”。

此外,TCO 作为一种模型是一种做事方式,但不是唯一的方式。对于任何可以编写为 TCO 递归应用程序的东西,将其重构为基于循环的非递归算法通常并不是那么困难。与基于收益的异步操作相比,您当然可以重写(嘿,这都是图灵机),但重写会很困难,并且生成的代码相当难以理解。我不想讨论产量/异步风格编码的价值(或缺乏价值),只是指出 TCO 没有那种“啊,但是,ifTCO 是个好主意,那么只有 TCO 才行”。

我没有现成的链接,但是那些对 Java 的未来有相当大影响力的人(例如 Brian Goetz、Mark Reinhold 等)已经说过类似的言论。如果您真的致力于尝试将其添加到 java 中,我建议您在网络上搜索这些陈述,然后尝试专门形成一些论据来解决它们所陈述的问题。因为如果你不能说服那些人,这永远不会发生。

那么我在java中做什么呢?

不要使用递归;使用while or for反而。

更新:那篇博客文章怎么样?

在您链接到的评论中这个博客条目。那是..不是TCO。

这就是使用 lambda 编写一个框架,让您或多或少地模拟 TCO,但它不是 TCO。该博客描述了一个小框架 - 因此,您需要他们粘贴的所有内容:特别是 TailCall 接口。

该代码的工作原理如下:

  • 您的“递归”方法根本不是递归的,它总是快速返回而不调用自身。
  • 不过,它返回一个可以调用自身的 lambda。但是,正如我们刚刚介绍的,调用自己可以快速返回而无需递归,并且它返回一个函数。
  • 框架将执行您的函数,该函数通常会生成一个函数(或实际结果)。它循环(所以没有递归),重复应用以下过程:“调用函数。如果它返回一个函数,则循环。如果它返回一个结果,好吧,这就是我们想要的结果,所以只需返回它”。

这描述了 TCO 试图完成的任务(使用不同的参数一遍又一遍地重复调用相同的函数,直到达到硬编码的边缘情况,然后反向退出),但不使用 TCO 来完成它。

因此,那篇博客文章说“看,Java 中的 TCO!”具有误导性。

就像我说的:“看,隧道墙上的画笔!”并描述如何使用cans喷漆以这样的方式涂漆隧道壁looks就像是用手刷过的一样。这很好,但称其为“刷墙”是有误导性的。充其量你可以说:“想要在隧道中制作画笔风格的艺术?好吧,你不能,我也无法解决这个问题,但我可以告诉你如何获得类似的结果!”。


*)永远不要说永远不会,但我的意思是:目前还没有任何计划,并且 Java 平台的未来计划要持续很多年,并且是相当公开的。我对“java(语言)在 4 年内没有尾调用递归”有 1 到 40 的赔率,并且仍然接受这个赌注。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java中的尾递归函数仍然会破坏堆栈 的相关文章

  • 如何将变量的全部内容发送/导出到文本文件/xml 文件/剪贴板?

    我想将实例的内容 最好以树形形式 发送给某人 打印屏幕是不行的 因为类太复杂了 您需要将输出转回实例吗 在这种情况下 其他答案都是正确的 如果您只想手动检查实例的内容 理想情况下您的类都将实现toString 你可以将其重定向到一个文件 如
  • 在Java中将*s打印为三角形?

    我在 Java 课程中的作业是制作 3 个三角形 一份左对齐 一份右对齐 一份居中 我必须为什么类型的三角形制作一个菜单 然后输入需要多少行 三角形必须看起来像这样 到目前为止 我能够完成左对齐的三角形 但我似乎无法获得其他两个 我尝试用谷
  • ScheduledThreadPoolExecutor如何在特定时间运行任务?

    特别是 它是否像这样在内部实现了 while true 循环 while System currentTimeMillis lt timeToRunTask Thread sleep 1000 doTask From http grepco
  • 如何使用 Maven Failsafe 插件运行 JUnit 5 集成测试?

    当我运行命令时 Maven Failsafe 插件找不到我的 JUnit 5 集成测试mvn clean failsafe integration test 尽管它可以找到文件 我有junit jupiter api and junit j
  • 如何在Java中优雅地处理SIGKILL信号

    当程序收到终止信号时如何处理清理 例如 我连接到一个应用程序 希望任何第三方应用程序 我的应用程序 发送finish注销时的命令 发送该信息最好说什么finish当我的应用程序被破坏时的命令kill 9 编辑1 kill 9无法被捕获 谢谢
  • 如何将现有的 SQLite3 数据库导入 Room?

    好吧 我在桌面上使用 SQLite3 创建了一个只需要读取的某些信息的数据库 我正在制作的应用程序不需要在此表中插入或删除信息 我在 Room 数据库层上做了相当多的谷歌搜索 所有文档都需要在构建应用程序时在 Room 中创建一个新的数据库
  • OpenNLP 与斯坦福 CoreNLP

    我一直在对这两个包进行一些比较 但不确定该往哪个方向走 我简单地寻找的是 命名实体识别 人 地点 组织等 性别识别 一个不错的训练 API 据我所知 OpenNLP 和斯坦福 CoreNLP 提供了非常相似的功能 然而 Stanford C
  • 在 Junit 测试中使用 ReflectionTestUtils.setField()

    我是 JUnittesting 的新手 所以我有一个问题 谁能告诉我为什么我们使用ReflectionTestUtils setField 在我们的 Junit 测试示例中 正如评论中提到的 java 文档很好地解释了用法 但我还想给你们举
  • 在 Java 中创建 T 的新实例

    在C 中 我们可以定义一个泛型class A
  • java.lang.Object的hashCode具体使用的算法是什么

    中使用的算法是什么JVM实施java lang Object的隐含的hashCode 方法 OpenJDK or Oracle JDK答案中首选 它依赖于实现 并且在很大程度上 该算法是entirely取决于实施 只要它是一致的 但是 根据
  • 将现有 eclipse 项目导出到 war 文件时出现“模块名称无效”

    我正在尝试将现有 Eclipse 项目导出到 war 文件 但无论我在 WAR Export 对话框页面中输入什么 系统总是返回 模块名称无效 我不知道如何解决这个问题 谢谢您的帮助 我有同样的问题 我修复了它 请按照以下步骤操作 您可以创
  • 更改 JComboBox 中滚动条的大小

    有谁知道如何手动更改 jComboBox 中的滚动条大小 我已经尝试了一大堆东西 但没有任何效果 好吧 我明白了 您可以实现 PopUpMenuListener 并使用它 public void popupMenuWillBecomeVis
  • 改变 Java 中凯撒移位的方向

    用户可以通过选择 1 向左或 2 向右移动字母来选择向左或向右移动 左边工作正常 右边不行 现在它显示了完全相同的循环 但我已经改变了所有 and 以不同的方式进行标记 最终我总是得到奇怪的字符 如何让程序将字符向相反方向移动 如果用户输入
  • Java8:流映射同一流中的两个属性

    我有课Model带有以下签名 class Model private String stringA private String stringB public Model String stringA String stringB this
  • 如何检查日期字符串的有效性?

    在我的项目中 我需要检查日期字符串是否计算为正确的日期对象 我决定允许 yyyy MM dd 和日期格式 年 月 日 和 年 月 日 小时 分钟 我如何检查它们是否有效 我的代码为 1980 01 01 和一些奇怪的日期 如 3837 05
  • 从浏览器访问本地文件?

    您好 我想从浏览器访问系统的本地文件 由于涉及大量安全检查 是否可以通过某种方式实现这一目标 或使用 ActiveX 或 Java Applet 的任何其他工作环境 请帮帮我 要通过浏览器访问本地文件 您可以使用签名的 Java Apple
  • android 中的 java.net.URL ..新手问题

    我是java新手 正在尝试android开发 以下代码生成 malformedURLException 有人可以帮助我识别异常吗 任何提示都会非常有帮助 package com example helloandroid import and
  • 在 Freemarker 模板中检查 Spring 安全角色和记录的用户名

    有谁知道 freemarker 标签来检查 freemarker 文件中的 spring 安全角色和用户名 我从网络上的几个资源中发现以下代码将打印登录的用户名 但它没有打印用户名 而是打印 登录为
  • java.lang.ClassCastException:com.sun.proxy.$Proxy8 无法转换为 org.openqa.selenium.internal.WrapsDriver

    我有以下切入点和 AspectJ 中给出的建议 Pointcut call org openqa selenium WebElement sendKeys public void onWebElementAction After onWeb
  • 如何使用 Jest 从 ElasticSearch 获取索引列表

    我正在尝试使用 Jest 检索索引列表 但我只得到 Stats statistics new Stats Builder build result client execute statistics 如何从结果中检索索引列表 除了统计之外

随机推荐

  • 使用firebase树结构直接表示“文档大纲”结构

    使用 Firebase 树结构来进行该操作是多么的好 愚蠢啊directly代表一个面向用户的树形结构 就像 文字处理器 中的 文档大纲 与例如相反执行 SQL 连接父子类型的关系 然后通过投影构建树 这可能会很慢 我知道嵌套的层数限制为
  • 如何让这两个div并排?

    我有两个未嵌套的 div 一个在另一个之下 它们都位于一个父 div 内 并且该父 div 会重复自身 所以本质上 div div class child div 1 div div class child div 2 div div di
  • Android OpenGL ES2.0 纹理交换

    首先 我是 OpenGL 新手 但在我的手机 摩托罗拉 Bionic 上 以下代码按预期工作 GLES20 glActiveTexture GLES20 GL TEXTURE1 GLES20 glBindTexture GLES20 GL
  • Photoshop CS5 无法识别 activeDocument

    我在 64 位 Vista 机器上为 Photoshop CS5 1 编写了一个相当大的脚本 现在 当我在新的 64 位 Windows 7 计算机上运行完全相同的脚本时 Adobe ExtendScript Tool 会抱怨activeD
  • 如何在Mule中读取巨大的CSV文件

    我正在使用 Mule Studio 3 4 0 社区版 我有一个关于如何解析通过文件端点传入的大型 CSV 文件的大问题 场景是我有 3 个 CSV 文件 我会将文件的内容放入数据库中 但是当我尝试加载一个大文件 大约 144MB 时 我收
  • 使用 Game Center 中的新重赛 WithCompletionHandler 方法时出现问题

    我正在使用 gamecenter api 制作回合制游戏 我想制作一个一键按钮来重新匹配玩家 这样他们就不必通过游戏中心视图控制器并重新邀请同一玩家 在这个问题中iOS 游戏套件 回合制比赛 程序化复赛提问者后来指出 ios 6 0 使用
  • 并行事件处理程序

    我想立即通知班级的活动订阅者 我应该推出自己的事件处理程序吗 使用FCL中的一些支持并行性 或默认内置System EventHandler
  • 如何在Android中以编程方式隐藏SD卡中的文件夹

    我有一个带有 xyz zip 文件夹的应用程序 该文件夹在安装到 SD 卡后解压 文件夹包含很多文件 例如视频 音频等 在安装应用程序期间 我想隐藏该文件夹并隐藏文件夹的所有项目 提前致谢 任何答案表示赞赏 使用这样的函数 public s
  • 如何在 QuickBooks API 中检索 PDF 格式的发票?

    使用 IPP NET SDK v2 0 1 需要将 PDF 版本的发票作为文件获取 就像通过电子邮件发送给客户一样 我有Invoice已从服务中检索到对象 如何才能做到这一点 QBO V3 API 服务尚不支持 此功能应包含在未来的 API
  • 添加ScrollView后,应用标题不再显示

    您好 在下面的代码中我有一个登录布局 其中包含电子邮件 ID 和密码是属性 现在想以横向模式显示我的布局 因为我刚刚添加了滚动视图 现在我的问题是在将滚动视图添加到我的布局 LOGIN 标题后不显示 谁能帮帮我吗
  • Laravel 模型条件格式

    我有一个名为 Vote actions 的数据库和模型 如下所示 id group id user id 动作类型 匿名 布尔值 用户可以要求匿名 这将使布尔值变为 true 如果是这种情况 我想将返回模型中的 group id 和 use
  • 两个内容相同的字符串会存储在同一个内存位置吗?

    这是我在采访中得到的一个问题 我有两个字符串定义为 String s1 Java String s2 Java 我的问题是这两个引用是否指向同一内存位置 一般来说 当我们创建相同的字符串 没有 new 关键字 时 内容是否只在内存中存储一
  • ParserError:语法错误,位于第 1 行、第 23 列

    当我将 React 项目部署到 Netlify 时 出现以下错误 在此输入图像描述 节点 1551 DEP0148 DeprecationWarning 在包的 导出 字段模块解析中使用已弃用的文件夹映射 位于 opt build repo
  • 如何将 Symfony 自动装配与多个实体管理器一起使用

    我想在使用 2 个不同实体管理器的服务中使用自动装配 如何实现这样的目标 use Doctrine ORM EntityManager class TestService public function construct EntityMa
  • 搜索整个 postgres 数据库? [复制]

    这个问题在这里已经有答案了 我有一个庞大的数据库 我不介意留下来搜索一段时间 但由于各种原因我无法转储整个数据库 我可以编写的最简单的查询是什么 它将在数据库中所有表的所有字段中搜索特定的文本字符串 以下内容不起作用 但应该展示我想看到的内
  • 如何在 uiwebview 中使用 javascript 获取单击的按钮 id?

    这次我想知道如何确定 UIWebView 中点击了哪个按钮 appDelegate mystring NSMutableString string init NSString buttonstring
  • calloc 或 malloc 可以用于在 OSX 中仅分配物理内存吗?

    我正在使用 c 函数 malloc 和 calloc 我有一些问题 我想看看是否可以使用这两个函数仅分配物理内存 我的mac有4gb或ram 当我使用malloc时 我可以分配超过4gb的内存 这意味着malloc同时分配物理内存和虚拟内存
  • 如何从ObjectInputStream读取所有对象

    我有一个包含一些信息的文件 如何读取所有信息 Name names try FileInputStream fileInputStream new FileInputStream file ObjectInputStream objectI
  • 尝试使用 PHP 和 fpdf 显示 pdf 文件

    背景 这是一个电子学习网站 当用户处于学习阶段时 它会在前3页显示供阅读的内容 之后需要用户进行测试 当用户克服所有挑战后 它会显示pdf证书 目前的问题是 到达最后一页后 它总是显示一些奇怪的单词 以下是全部 PDF 1 3 3 0 ob
  • Java中的尾递归函数仍然会破坏堆栈

    我正在尝试实现尾递归阶乘计算器 但仍然出现堆栈溢出 谁能帮我找出原因吗 我读过 Java 8 支持 Tail 调用优化 但我想我一定没有正确实现它 我读到可以使用 lambda 表达式 我不确定我是否完全理解这个概念 但我仍在阅读 我只是在