在Racket中将结构递归转化为累积递归

2024-05-14

我有一些代码来查找最大高度并将其替换为关联的名称。身高和姓名有单独的列表,每个列表的长度相同且非空。

我可以使用结构递归来解决这个问题,但必须将其更改为累积递归,而且我不确定如何做到这一点。我见过的所有例子都让我困惑。有人能够将代码变成使用累积递归的代码吗?

(define (tallest names heights)
  (cond
    [(empty? names) heights]
    [(> (first heights) (first (rest heights)))
     (cons (first names) (tallest (rest (rest names)) (rest (rest heights))))]
    [else (tallest (rest names) (rest heights))]))

首先,您提供的tallest函数实际上不起作用(调用(tallest '(Bernie Raj Amy) (list 1.5 1.6 1.7))因合同错误而失败),但我明白你的意思。结构递归和累积递归有什么区别?


好吧,结构递归的工作原理是构建一个结构作为返回值,其中该结构内的值之一是对同一函数的递归调用的结果。以阶乘的递归计算为例。你可以这样定义它:

(define (factorial n)
  (if (zero? n) 1
      (* n (factorial (sub1 n)))))

想象一下这个程序如何针对输入执行,例如:4。每次调用都会在乘法表达式中留下一个“洞”,以便由递归子调用的结果填充。这就是看起来的样子,可视化,使用_代表这些“洞”之一。

(* 4 _)
     (* 3 _)
          (* 2 _)
               (* 1 _)
                    1

注意大部分工作是如何完成的仅在那之后最终情况已达成。大部分工作必须在调用返回时将其从堆栈中弹出的过程中完成,因为每个调用都依赖于对其子调用结果执行一些附加操作。


累积递归有何不同?好吧,在累积递归中,我们对函数使用一个额外的参数,称为 an累加器。重写上面的阶乘函数以使用累加器将使其看起来像这样:

(define (factorial n acc)
  (if (zero? n) acc
      (factorial (sub1 n) (* acc n))))

现在如果我们想求 4 的阶乘,我们必须调用(factorial 4 1),为累加器提供一个起始值(稍后我将讨论如何避免这种情况)。如果您现在考虑一下调用堆栈,它看起来会完全不同。

请注意,没有“漏洞”需要填补——结果如下:factorial函数要么是累加器,要么是对其自身的直接调用。这被称为尾调用,以及递归调用factorial被称为处于尾部位置.

事实证明这很有帮助,原因有几个。首先,有些函数用累积递归更容易表达,但factorial可能不是其中之一。然而,更重要的是,Scheme 要求实现提供正确的尾部呼叫(有时也称为“尾部调用优化”),这意味着进行尾部调用时调用堆栈的深度不会增长。

关于尾部调用如何工作以及它们为何有用的现有信息有很多,因此我不会在这里重复。重要的是要理解累计递归涉及到累加器参数,这通常会导致结果函数通过尾部调用来实现。

但是对于额外的参数我们该怎么办呢?好吧,我们实际上可以创建一个“辅助”函数来执行累积递归,但我们将提供一个自动填充基本情况的函数。

(define (factorial n)
  (define (factorial-helper n acc)
    (if (zero? n) acc
        (factorial-helper (sub1 n) (* acc n))))
  (factorial-helper n 1))

这种习惯用法很常见,以至于 Racket 提供了一个“命名的let" 形式,将上面的函数简化为:

(define (factorial n)
  (let helper ([n n] [acc 1])
    (if (zero? n) acc
        (helper (sub1 n) (* acc n)))))

但这只是同一想法的一些语法糖。


可以,然后呢:这些如何适用于你的问题?嗯,实际上,使用累积递归使解决问题变得非常容易。以下是如何构建算法的详细说明:

  1. 就像在原来的示例中一样,您将迭代列表,直到得到empty。这将构成您的“基本案例”。
  2. 你的累加器会很简单——它将是当前最大元素你已经找到了。
  3. 每次迭代时,如果发现一个元素大于当前最大值,则该元素将成为新的累加器。否则,累加器保持不变。

将这些放在一起,这是一个简单的实现:

(define (tallest-helper names heights current-tallest)
  (cond
    [(empty? names)
     (car current-tallest)]
    [(> (first heights) (cdr current-tallest))
     (tallest-helper (rest names) (rest heights)
                     (cons (first names) (first heights)))]
    [else
     (tallest-helper (rest names) (rest heights)
                     current-tallest)]))

这可以通过多种方式进一步改进 - 将其包装在提供累加器起始值的函数中,使用命名let、删除一些重复内容等等——但我会将其作为练习留给您。

请记住:累加器实际上是您的“工作总和”。这是您的“运行总计”。明白了这一点,事情就应该有意义了。

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

在Racket中将结构递归转化为累积递归 的相关文章

  • 在Racket中将结构递归转化为累积递归

    我有一些代码来查找最大高度并将其替换为关联的名称 身高和姓名有单独的列表 每个列表的长度相同且非空 我可以使用结构递归来解决这个问题 但必须将其更改为累积递归 而且我不确定如何做到这一点 我见过的所有例子都让我困惑 有人能够将代码变成使用累
  • 如何用流程图表示递归函数?

    我需要在流程图上表示递归函数 我的问题是我不知道如何指示该函数可以一次在多个元素上调用自身 例如扫描图形的函数 有人有什么建议吗 在流程图中 您通常不会为循环之类的内容添加多次调用 您只需指示可以重复调用代码 直到满足条件为止 因此 对于递
  • 为什么Racket中foldl的定义方式很奇怪?

    在 Haskell 中 与许多其他函数式语言一样 函数foldl被定义为 例如 foldl 0 1 2 3 4 10 这没关系 因为foldl 0 1 2 3 4 根据定义 0 1 2 3 4 但是 在 球拍 中 foldl 0 1 2 3
  • 我在函数的最后一次递归调用中得到“方案应用程序而不是过程”

    所以这是代码 define time prime test n newline display n start prime test n runtime define start prime test n start time if pri
  • 使用fold_left/right反转OCaml中的列表

    更新 解决方案 感谢 jacobm 的帮助 我想出了一个解决方案 Folding Recursion let reverse list 3 theList List fold left fun element recursive call
  • @tailrec为什么这个方法不编译为“包含不在尾部位置的递归调用”?

    tailrec private def loop V key String V key match case gt loop key 此方法无法编译并抱怨它 包含不在尾部位置的递归调用 有人可以向我解释一下发生了什么事吗 这个错误消息对我来
  • 合并xml文档

    我遇到的所有关于合并 XML 文档的解决方案都无法实现我的愿望 让我解释 XML 文档 1 a b title Original Section b title Original Child Section b b title Origin
  • Python如何处理无限递归?

    因此 在使用 Python 时 我注意到程序的堆栈大小基本上没有限制 继续对数字执行幂运算 即使在达到数千位之后 精度仍然保持完美 这让我想知道 如果我不小心进入了Python的无限递归循环怎么办 编译器会注意到并抛出堆栈溢出错误吗 或者程
  • 仅使用堆区域的递归

    是否有仅使用堆区域的递归示例 在 C 中 基于函数调用的递归总是使用堆栈 几乎按照定义 如果您愿意将递归转换为迭代 那么可以仅使用堆空间 但这并不是真正的递归 您可以通过在堆中实现堆栈来实现这一点 某些问题可以使用尾递归 http en w
  • 递归检查字符串中的所有字母是否都是大写

    我必须检查递归中所有字母是否都是大写字母 我不知道为什么这不起作用 public static bool IsCapital string str if str Length 1 return int Parse str 0 ToStrin
  • 递归 lambda 表达式可能吗?

    我正在尝试编写一个调用自身的 lambda 表达式 但我似乎找不到任何语法 或者即使它是可能的 本质上我想将以下函数传输到以下 lambda 表达式中 我意识到这是一个愚蠢的应用程序 它只是添加 但我正在探索可以在 python 中使用 l
  • 可扩展的宏定义

    灵感来自于评论区 https stackoverflow com questions 23879410 is it possible to extend a function lambda macro in scheme 23879575
  • 返回列表的前 n 个

    如何返回第一个n列表的元素 这是我所拥有的 define returns lambda list n cond null list 0 n n 1 car list cons car list returns cdr list n else
  • 如何在 DrScheme 中包含文件?

    我正在使用 DrScheme 来完成 SICP 并且我注意到某些程序 例如 square 一遍又一遍地使用 我想将它们放在一个单独的文件中 以便我可以将它们包含在其他程序中 而不必每次都重写它们 但我似乎不知道如何做到这一点 我试过了 lo
  • 忽略 Racket 中的多个返回值

    在 Racket 中 可以通过执行以下操作从函数返回多个值 define foo values 1 2 3 然后我们可以通过这样做来绑定它们 define values one two three foo Now one一定会1 two t
  • 生成一定长度的所有排列

    假设我们有一个字母表 abcdefghiklimnop 如何以有效的方式以五个一组的形式重复该字母表来递归生成排列 几天来我一直在为此苦苦挣扎 任何反馈都会有帮助 本质上这与 生成给定字符串的所有排列 https stackoverflow
  • Fortran 递归分段错误

    我必须设计并实现一个 Fortran 例程来确定方格上簇的大小 并且递归地编写子例程似乎非常方便 然而 每当我的晶格大小超过某个值 大约 200 边 时 子例程就会始终出现段错误 这是我的集群检测例程 RECURSIVE SUBROUTIN
  • 如何使用 DrRacket 遵循 SimplyScheme 书籍

    我希望能够操纵句子 以便我可以将它们作为输入并根据单个字母等内容返回输出 例如 ends e 命令将返回所有以 e 结尾的单词 ends e only the good die young gt the die 不幸的是 e 是一个字符串
  • 如何从 Unix 命令行递归解压目录及其子目录中的档案?

    The unzip命令没有递归解压缩档案的选项 如果我有以下目录结构和档案 Mother Loving zip Scurvy Sea Dogs zip Scurvy Cures Limes zip 我想将所有档案解压缩到与每个档案同名的目录
  • 有没有相互递归的例子?

    是否有递归函数调用另一个函数 该函数也调用第一个函数 的示例 例子 function1 do something function2 do something function2 do something function1 do some

随机推荐

  • SimpleMembershipProvider 在 WebSecurity.SignOut 之后不会销毁会话

    我正在使用所有默认成员代码运行 ASP NET MVC 4 AccountController的LogOff的代码是 HttpPost ValidateAntiForgeryToken public ActionResult LogOff
  • 给定的 System.Uri 无法转换为 Windows.Foundation.Uri

    我正在尝试以编程方式在 XAML Metro 应用程序中加载 BitmapImage 这是我的代码 var uri new Uri Images 800x600 BackgroundTile bmp UriKind RelativeOrAb
  • 如何在 Oracle 上生成版本 4(随机)UUID?

    该博客解释说 输出sys guid 对于每个系统来说不是随机的 http feuerthoughts blogspot de 2006 02 watch out for sequential oracle guids html http f
  • 无法将matplotlib安装到pycharm

    我最近开始使用Python速成课程学习Python编程 我陷入困境 因为我无法让 matplotlib 在 pycharm 中工作 我已经安装了pip 我已经通过命令提示符使用 pip 安装了 matplotlib 现在 当我打开 pych
  • 如何使用 jest 通过 Promise.all 设置多次提取测试

    我在测试中使用 jest 我正在使用 React 和 Redux 并且执行以下操作 function getData id notify return dispatch gt dispatch anotherFunction Promise
  • 我应该在 PHP 代码中使用断言吗?

    一位同事添加了assert http php net assert在我们的库中 在我本来会使用 if 语句并引发异常的地方执行几次命令 在此之前我什至从未听说过断言 以下是他如何使用它的示例 assert isset this gt rec
  • Rails 4 - 如何链接到 PDF 文件(名称.PDF)?

    我正在生成 PDF 文件 我的链接如下所示 当我点击这个时 它会带我去 display invoice 123456789 这是一个 HTML 版本 在控制器中的操作如下 def display invoice if params invo
  • 文本视图不显示全文

    我正在使用 TableLayout 和 TableRow 创建一个简单的布局 其中包含两个 TextView 这是代码的一部分
  • 如何更改元素的 CSS 类并在单击时删除所有其他类

    我如何处理 AngularJS 2 中的一种情况 即单击一个元素需要更改其自己的样式 并且如果其他元素具有该样式 则需要将其删除 最好在一个函数中 如同Angular js 如何在单击时更改元素 css 类并删除所有其他元素 https s
  • 如何在html中设置按钮的文本大小

    您好 我想在我的网站上有一个按钮 并且我想调整按钮上的文本大小 我该怎么做呢 我的代码如下
  • 增加内存限制时出现奇怪的错误

    我使用的是共享托管环境 PHP 的默认内存限制是 32M 我在 Concrete5 设置方面遇到一些问题 当我尝试登录 Concrete5 的管理面板时 出现内存限制错误Allowed memory size of 33554432 byt
  • 将带有 glut 的点击坐标添加到向量链接列表中

    我想创建一个向量链接列表 并在 GLUT 库的帮助下获取点击的位置并将它们附加到链接列表中 这些是我写的结构 typedef struct vector int x int y Vector typedef struct VectorLis
  • 在 Spring Security ACL 中授予权限

    我在 grails 1 3 7 中使用 Spring Security ACL 插件 但我的问题可能比这更通用 我想允许拥有以下权限的用户BasePermission READ访问对象以便能够向其他用户授予相同的权限 如果 user1 具有
  • 如何在 C++ 中将 CString 转换为 double?

    我如何转换CString to a double在 C 中 Unicode 支持也很好 Thanks A CString可以转换为LPCTSTR 这基本上是一个const char const wchar t 在 Unicode 版本中 知
  • Rails 中的 PDF 导出

    我需要将包含一些图表的 HTML 页面导出为 PDF 有哪些好的 gem 可以做到这一点 PDFKit http railscasts com episodes 220 pdfkit http railscasts com episodes
  • 两种类型的回发事件

    1 我发现了两篇文章 每篇文章对两种类型的回发事件的分类都略有不同 一位资源说两种类型的回发事件是Changed事件 其中控件实现 IPostbackDataHandler 当数据在回发之间更改时触发 然后Raised事件 其中控件实现 I
  • 更改用作函数全局作用域的字典

    我想做一个 purePython 的装饰器 其中一部分是能够有选择地禁止访问函数的全局范围 有没有一种方法可以以编程方式更改哪个字典事物充当函数的全局 外部作用域 因此 例如在下面我希望能够拦截对f in h并抛出错误 但我想允许访问g因为
  • 异步异常处理程序:在事件循环线程停止之前不会被调用

    我正在我的异步事件循环上设置异常处理程序 但是 在事件循环线程停止之前 它似乎不会被调用 例如 考虑以下代码 def exception handler loop context print Exception handler called
  • Lombok 不适用于 Eclipse Neon

    我下载了lombok jar lombok 1 16 14 jar 并将其放入我的下载中 然后我点击这个 jar 执行正确地识别了我的 MacOS 上的 Eclipse 实例 然后我选择了我想要的实例 Lombok也在pom xml中指定
  • 在Racket中将结构递归转化为累积递归

    我有一些代码来查找最大高度并将其替换为关联的名称 身高和姓名有单独的列表 每个列表的长度相同且非空 我可以使用结构递归来解决这个问题 但必须将其更改为累积递归 而且我不确定如何做到这一点 我见过的所有例子都让我困惑 有人能够将代码变成使用累