如果没有,是否有一个好的计数器示例显示迭代算法,其中不存在递归对应物?
如果所有迭代算法都可以递归表达,那么是否存在更难以做到的情况呢?
此外,编程语言在这一切中扮演什么角色?我可以想象Scheme程序员对Java迭代(=尾递归)和堆栈使用有不同的看法。
答案 0 :(得分:69)
这是一个简单的临时证明。既然你可以使用严格的迭代结构和仅使用递归结构的转换完整语言来构建图灵完整语言,那么这两者就是等价的。
答案 1 :(得分:18)
所有迭代算法都可以递归表达吗?
是的,但证据并不有趣:
将程序及其所有控制流转换为包含单个case语句的单个循环,其中每个分支都是直线控制流,可能包括break
,return
,{{1 },exit
等等。引入一个新变量(称之为“程序计数器”),case语句用它来决定下一个要执行的块。
这种结构是在20世纪60年代伟大的“结构化编程战争”中发现的,当时人们在争论各种控制流构造的相对表现力。
用递归函数替换循环,并用参数替换每个可变局部变量到该函数。瞧!迭代取代了递归。
此过程相当于为原始函数编写解释器。正如您可能想象的那样,它会导致无法读取的代码,并且这不是一件有趣的事情。 然而,有些技术对于具有命令式编程背景的人来说非常有用,他们正在学习第一次使用函数式语言进行编程。
答案 2 :(得分:8)
就像你说的,每个迭代方法都可以变成一个“递归”方法,并且通过尾调用,堆栈也不会爆炸。 :-)实际上,这实际上是Scheme如何实现所有常见的循环形式。方案中的例子:
(define (fib n)
(do ((x 0 y)
(y 1 (+ x y))
(i 1 (+ i 1)))
((> i n) x)))
这里,尽管函数看起来是迭代的,但它实际上是在内部lambda上进行递归,该lambda带有三个参数x
,y
和i
,并在每个参数上调用自己的新值迭代。
这是函数可以宏扩展的一种方式:
(define (fib n)
(letrec ((inner (lambda (x y i)
(if (> i n) x
(inner y (+ x y) (+ i 1))))))
(inner 0 1 1)))
这样,递归性质在视觉上变得更加明显。
答案 3 :(得分:6)
将迭代定义为:
function q(vars):
while X:
do Y
可翻译为:
function q(vars):
if X:
do Y
call q(vars)
在大多数情况下,Y会包括递增一个由X测试的计数器。当进行递归路由时,这个变量必须以某种方式传递给'vars'。
答案 4 :(得分:1)
如their answer中的plinth所述,我们可以构建证明recursion和迭代是如何等效的证明,并且可以用来解决同样的问题;然而,即使我们知道这两者是相同的,但是使用一个相对于另一个存在缺点。
在未针对递归进行优化的语言中,您可能会发现使用迭代的算法比递归算法更快,同样,即使在优化语言中,您可能会发现使用以不同语言编写的迭代的算法运行速度比递归更快一。此外,可能没有使用递归与迭代编写给定算法的明显方法,反之亦然。这可能导致难以阅读的代码导致可维护性问题。
答案 5 :(得分:-1)
Prolog是递归的唯一语言,你可以在其中做很多事情(我不建议你做,但你可以:))
答案 6 :(得分:-3)
递归解决方案通常效率相对较低。 然而,值得注意的是,有一些问题只能通过递归来解决,并且等效迭代解决方案可能不存在或者编程非常复杂(例如 Ackermann函数无法在没有递归的情况下表达虽然递归是优雅的,易于编写和理解。