Recursion 球拍中的结构递归到累加递归
我有一些代码来查找最大高度,并用关联的名称替换它。高度和名称有单独的列表,每个列表长度相同且不为空 我可以使用结构递归来解决这个问题,但必须将其转换为累加,我不确定如何做到这一点。我看到的所有例子都让我困惑。有人能用累加递归将代码转换成一个吗Recursion 球拍中的结构递归到累加递归,recursion,scheme,racket,Recursion,Scheme,Racket,我有一些代码来查找最大高度,并用关联的名称替换它。高度和名称有单独的列表,每个列表长度相同且不为空 我可以使用结构递归来解决这个问题,但必须将其转换为累加,我不确定如何做到这一点。我看到的所有例子都让我困惑。有人能用累加递归将代码转换成一个吗 (define (tallest names heights) (cond [(empty? names) heights] [(> (first heights) (first (rest heights))) (co
(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))]))
首先,您提供的
highest
函数实际上不起作用(调用(highest'(Bernie Raj Amy)(列表1.5 1.6 1.7))
失败,出现合同错误),但我明白您的意思。结构递归和累积递归有什么区别
(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))]))
结构递归的工作原理是将结构构建为返回值,其中该结构中的一个值是对同一函数的递归调用的结果。以阶乘的递归计算为例。您可以这样定义它:
(define (factorial n)
(if (zero? n) 1
(* n (factorial (sub1 n)))))
(define (factorial n acc)
(if (zero? n) acc
(factorial (sub1 n) (* acc n))))
可视化该程序如何执行输入,例如,4
。每次调用都会在乘法表达式中留下一个“洞”,由递归子调用的结果填充。下面是用\uu
表示其中一个“洞”的可视化效果
请注意,大部分工作是如何在最终案例完成后才完成的。大部分工作必须在调用返回时弹出堆栈的过程中完成,因为每个调用都取决于对其子调用的结果执行一些额外的操作
累积递归有何不同?在累加递归中,我们对函数使用了一个额外的参数,称为累加器。重写上述阶乘函数以使用累加器将使其如下所示:
(define (factorial n)
(if (zero? n) 1
(* n (factorial (sub1 n)))))
(define (factorial n acc)
(if (zero? n) acc
(factorial (sub1 n) (* acc n))))
现在,如果我们想找到4的阶乘,我们必须调用(阶乘41)
,为累加器提供一个起始值(稍后我将讨论如何避免)。如果您现在考虑调用堆栈,它看起来会完全不同
请注意,factorial
函数的结果是累加器还是对自身的直接调用,没有需要填充的“漏洞”。这被称为尾部调用,对factorial
的递归调用被称为处于尾部位置
事实证明,出于几个原因,这是有帮助的。首先,有些函数更容易用累加递归来表示,尽管阶乘可能不是其中之一。然而,更重要的是,该方案要求实现提供适当的尾部调用(有时也称为“尾部调用优化”),这意味着在进行尾部调用时,调用堆栈不会深入增长
关于尾部调用如何工作以及它们为什么有用,现有的信息很多,所以我在这里不再重复。重要的是要理解累加递归涉及累加器参数,这通常会导致结果函数通过尾部调用实现
但是对于额外的参数我们该怎么办呢?实际上,我们可以创建一个“helper”函数来进行累加递归,但我们将提供一个自动填充基本情况的函数
(define (factorial n)
(define (factorial-helper n acc)
(if (zero? n) acc
(factorial-helper (sub1 n) (* acc n))))
(factorial-helper n 1))
这种习惯用法非常常见,Racket提供了一种“namedlet
”形式,将上述函数简化为:
(define (factorial n)
(let helper ([n n] [acc 1])
(if (zero? n) acc
(helper (sub1 n) (* acc n)))))
但这只是对同一个想法的一些句法上的甜点
好的,那么:这些如何适用于你的问题?实际上,使用累积递归可以使问题的实现变得非常简单。下面是如何构建算法的分解图:
为空。这将构成你的“基本情况”
(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
,删除一些重复,等等-但我将把它留给您作为练习
请记住:累加器实际上是您的“工作总和”。这是你的“跑步总数”。理解这一点,事情就会有意义。我不太清楚这段代码应该做什么。你能给出一些输入和输出的示例吗?(检查expect(最高的(列出Bernie Raj Amy)(列出1.5 1.7 1.6))'Raj)