Programming languages 所有迭代算法都可以递归表示吗?

Programming languages 所有迭代算法都可以递归表示吗?,programming-languages,recursion,iteration,language-theory,Programming Languages,Recursion,Iteration,Language Theory,如果没有,是否有一个很好的反例显示了一个迭代算法,该算法不存在递归对应项 如果所有迭代算法都可以递归表示,那么有没有更难实现的情况 另外,编程语言在这一切中扮演什么角色?我可以想象Scheme程序员在迭代(=尾部递归)和堆栈使用方面与纯Java程序员不同。有一个简单的特别证明。由于您可以使用严格的迭代结构构建图灵完备语言,而图灵完备语言只使用递归结构,因此这两种语言是等价的。正如您所说,每种迭代方法都可以转换为“递归”方法,并且通过尾部调用,堆栈也不会爆炸。:-)实际上,Scheme就是这样实现

如果没有,是否有一个很好的反例显示了一个迭代算法,该算法不存在递归对应项

如果所有迭代算法都可以递归表示,那么有没有更难实现的情况


另外,编程语言在这一切中扮演什么角色?我可以想象Scheme程序员在迭代(=尾部递归)和堆栈使用方面与纯Java程序员不同。

有一个简单的特别证明。由于您可以使用严格的迭代结构构建图灵完备语言,而图灵完备语言只使用递归结构,因此这两种语言是等价的。

正如您所说,每种迭代方法都可以转换为“递归”方法,并且通过尾部调用,堆栈也不会爆炸。:-)实际上,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)))
通过这种方式,递归性质变得更加直观。

将迭代定义为:

function q(vars):
  while X:
    do Y
可以翻译为:

 function q(vars):
    if X:
      do Y
      call q(vars)

在大多数情况下,Y将包括递增一个由X测试的计数器。在进行递归时,这个变量必须以某种方式在“vars”中传递。

Prolog是一种仅递归的语言,您可以在其中执行几乎所有操作(我不建议您这样做,但您可以:))

正如普林斯在书中指出的那样,我们可以构造证明,证明和迭代是如何等价的,并且都可以用于解决相同的问题;然而,尽管我们知道两者是等价的,但使用其中一种方法还是有缺点的

在未针对递归进行优化的语言中,您可能会发现使用迭代的算法比递归算法执行得更快,同样,即使在优化的语言中,您也可能会发现使用不同语言编写的迭代的算法比递归算法运行得更快。此外,可能没有一种明显的方法可以使用递归和迭代编写给定的算法,反之亦然。这可能导致代码难以阅读,从而导致可维护性问题

所有迭代算法都可以递归表示吗

是的,但证据并不有趣:

  • 将程序及其所有控制流转换为包含单个case语句的单个循环,其中每个分支都是直线控制流,可能包括
    中断
    返回
    退出
    提升
    ,等等。引入一个新变量(称之为“程序计数器”),case语句使用该变量来决定下一步执行哪个块

    这种结构是在20世纪60年代的“结构化编程大战”中发现的,当时人们争论各种控制流结构的相对表达能力

  • 用递归函数替换循环,并用该函数的参数替换每个可变局部变量。瞧!迭代被递归所取代


  • 此过程相当于为原始函数编写一个解释器。正如您所想象的,这会导致无法读取的代码,而且这不是一件有趣的事情。但是,对于具有命令式编程背景的人来说,其中一些技术可能很有用,因为他们第一次学习用函数式语言编程。

    与迭代解决方案相比,递归解决方案通常效率较低。
    然而,需要注意的是,有些问题只能通过递归来解决,虽然递归很优雅,但是等价的迭代解可能不存在,或者编程起来非常复杂(例如阿克曼函数不能在没有递归的情况下表达),易于编写和理解。

    请注意,任何迭代算法都可以转化为尾部递归算法。例如,只需将其转换为延续传递样式。我要补充的是,并非所有语言编译器都会优化尾部调用,因此堆栈确实可以使用尾部递归(例如C#)在这些语言中“爆炸”(溢出)。哇,值得思考。我羡慕所有比我更快理解这一点的选民。是时候仔细阅读了。等等,这不是一个逻辑谬论吗?循环推理?鲍尔:事实上,它不是。证明非常容易:假设语言IT(仅限迭代构造)和REC(仅限递归构造)。使用它模拟通用图灵机,然后使用REC模拟通用图灵机。模拟器程序的存在保证了它和REC都可以计算所有可计算函数。lambda演算证明了此属性,其中所有函数都是部分递归的。或者,使用严格(迭代/递归)构建图灵完整语言,并在其中使用严格(递归/迭代)为图灵完整语言编写解释器。瞧,你所写的任何程序都是同时以迭代和递归的方式执行的!哦,@Norman,这是一件有趣的事。。对于编译器。实际上,过程是:将命令式代码转换为函数式代码,然后将函数式代码转换为命令式代码。为什么这很有趣?因为函数代码具有简单的语义,但无法执行,而命令式输出是不可理解的,但适合执行。特别是,函数式代码很容易针对高级内容进行优化,命令式代码很容易针对低级内容进行优化(但最初的混合很难用于任何目的)