第1,212行: |
第1,212行: |
| 由于重复的函数调用和返回,递归算法对于小规模数据往往效率不高。出于这个原因,递归算法的高效实现往往是先实现一个标准递归算法,但当输入变小时,再切换到不同的算法。一个重要的例子是归并排序,当数据足够小的时候,常常通过切换到非递归的插入排序来实现,如'''<font color="#ff8000">平铺合并排序 tiled merge sort</font>'''。混合递归算法往往可以进一步完善,如Timsort中,就是由混合归并排序/插入排序派生出来的。 | | 由于重复的函数调用和返回,递归算法对于小规模数据往往效率不高。出于这个原因,递归算法的高效实现往往是先实现一个标准递归算法,但当输入变小时,再切换到不同的算法。一个重要的例子是归并排序,当数据足够小的时候,常常通过切换到非递归的插入排序来实现,如'''<font color="#ff8000">平铺合并排序 tiled merge sort</font>'''。混合递归算法往往可以进一步完善,如Timsort中,就是由混合归并排序/插入排序派生出来的。 |
| | | |
− | ==Recursion versus iteration== | + | ==递归 VS 迭代== |
− | 递归和迭代
| + | |
| Recursion and [[iteration]] are equally expressive: recursion can be replaced by iteration with an explicit [[call stack]], while iteration can be replaced with [[tail call|tail recursion]]. Which approach is preferable depends on the problem under consideration and the language used. In [[imperative programming]], iteration is preferred, particularly for simple recursion, as it avoids the overhead of function calls and call stack management, but recursion is generally used for multiple recursion. By contrast, in [[functional programming|functional languages]] recursion is preferred, with tail recursion optimization leading to little overhead. Implementing an algorithm using iteration may not be easily achievable. | | Recursion and [[iteration]] are equally expressive: recursion can be replaced by iteration with an explicit [[call stack]], while iteration can be replaced with [[tail call|tail recursion]]. Which approach is preferable depends on the problem under consideration and the language used. In [[imperative programming]], iteration is preferred, particularly for simple recursion, as it avoids the overhead of function calls and call stack management, but recursion is generally used for multiple recursion. By contrast, in [[functional programming|functional languages]] recursion is preferred, with tail recursion optimization leading to little overhead. Implementing an algorithm using iteration may not be easily achievable. |
| | | |
− | 递归和迭代
| + | 递归和迭代的表达能力是一样的:递归可以用显式调用栈的迭代代替,而迭代可以用尾递归代替。哪种方法更合适,取决于所考虑的问题和所使用的语言。在命令式编程中,迭代是首选,特别是对于简单递归,因为它避免了函数调用的开销和调用栈管理,但在多重递归问题时通常还是要用到递归方法。相比之下,在函数式语言中,递归是首选的,因为尾递归优化只带来很少的开销。使用迭代实现算法可能不容易实现。 |
| | | |
− | 递归和迭代的表达方式是一样的:递归可以用显式调用堆栈的迭代代替,而迭代可以用尾递归代替。哪种方法更可取取决于所考虑的问题和所使用的语言。在命令式编程中,迭代是首选,特别是对于简单递归,因为它避免了函数调用的开销和调用堆栈管理,但递归通常用于多次递归。相比之下,在函数式语言中,递归是首选的,因为尾部递归优化只带来很少的开销。使用迭代实现算法可能不容易实现。
| + | Compare the templates to compute x<sub>n</sub> defined by x<sub>n</sub> = f(n, x<sub>n-1</sub>) from x<sub>base</sub>: |
| | | |
− | Compare the templates to compute x<sub>n</sub> defined by x<sub>n</sub> = f(n, x<sub>n-1</sub>) from x<sub>base</sub>:
| + | 比较x<sub>base</sub>中,由x<sub>n</sub> = f(n, x<sub>n-1</sub>)定义的计算x<sub>n</sub>的模板: |
− | 比较x<sub>base</sub>中x<sub>n</sub> = f(n, x<sub>n-1</sub>)定义的计算x<sub>n</sub>的模板: | |
| | | |
| {| | | {| |
第1,245行: |
第1,244行: |
| For example, a [[factorial]] function may be implemented iteratively in [[C (programming language)|C]] by assigning to an loop index variable and accumulator variable, rather than by passing arguments and returning values by recursion: | | For example, a [[factorial]] function may be implemented iteratively in [[C (programming language)|C]] by assigning to an loop index variable and accumulator variable, rather than by passing arguments and returning values by recursion: |
| | | |
− | 例如,在C语言中,一个阶乘函数可以通过分配给循环索引变量和累加器变量来反复实现,而不是通过递归传递参数和返回值。
| + | 例如,在C语言中,一个阶乘函数可以通过分配给循环索引变量和累加器变量来迭代实现,而不是通过递归传递参数和返回值。 |
| | | |
| <syntaxhighlight lang="C"> | | <syntaxhighlight lang="C"> |
第1,258行: |
第1,257行: |
| </syntaxhighlight> | | </syntaxhighlight> |
| | | |
− | ===Expressive power=== | + | ===表达能力=== |
− | 表现力
| + | |
| Most [[programming language]]s in use today allow the direct specification of recursive functions and procedures. When such a function is called, the program's [[runtime environment]] keeps track of the various [[Instance (computer science)|instance]]s of the function (often using a [[call stack]], although other methods may be used). Every recursive function can be transformed into an iterative function by replacing recursive calls with [[Control flow|iterative control constructs]] and simulating the call stack with a [[stack (data structure)|stack explicitly managed]] by the program.<ref>{{citation|title=Python Algorithms: Mastering Basic Algorithms in the Python Language|first=Magnus Lie|last=Hetland|publisher=Apress|date=2010|isbn=9781430232384|page=79|url=https://books.google.com/books?id=4cytGpIPYsAC&pg=PA79}}.</ref><ref>{{citation|title=Data Structures and Algorithms in C++|first=Adam|last=Drozdek|edition=4th|publisher=Cengage Learning|date=2012|isbn=9781285415017|page=197|url=https://books.google.com/books?id=PRgLAAAAQBAJ&pg=PA197}}.</ref> | | Most [[programming language]]s in use today allow the direct specification of recursive functions and procedures. When such a function is called, the program's [[runtime environment]] keeps track of the various [[Instance (computer science)|instance]]s of the function (often using a [[call stack]], although other methods may be used). Every recursive function can be transformed into an iterative function by replacing recursive calls with [[Control flow|iterative control constructs]] and simulating the call stack with a [[stack (data structure)|stack explicitly managed]] by the program.<ref>{{citation|title=Python Algorithms: Mastering Basic Algorithms in the Python Language|first=Magnus Lie|last=Hetland|publisher=Apress|date=2010|isbn=9781430232384|page=79|url=https://books.google.com/books?id=4cytGpIPYsAC&pg=PA79}}.</ref><ref>{{citation|title=Data Structures and Algorithms in C++|first=Adam|last=Drozdek|edition=4th|publisher=Cengage Learning|date=2012|isbn=9781285415017|page=197|url=https://books.google.com/books?id=PRgLAAAAQBAJ&pg=PA197}}.</ref> |
| | | |
− | 今天使用的大多数编程语言都允许直接指定递归函数和程序。当这样的函数被调用时,程序的运行时环境会跟踪该函数的各种实例(通常使用调用堆栈,尽管可能使用其他方法)。每一个递归函数都可以通过用迭代控制构造代替递归调用,并用程序显式管理的堆栈模拟调用堆栈来转化为迭代函数。
| + | 今天使用的大多数编程语言都允许直接编写递归函数和程序。当这样的函数被调用时,程序的运行时环境会跟踪该函数的各种实例(通常使用调用栈,也可能会使用其他方法)。每一个递归函数都可以被转换成迭代函数,用迭代控制构造代替递归调用,并用程序显式管理的自己的栈来模拟系统调用栈。<ref>{{citation|title=Python Algorithms: Mastering Basic Algorithms in the Python Language|first=Magnus Lie|last=Hetland|publisher=Apress|date=2010|isbn=9781430232384|page=79|url=https://books.google.com/books?id=4cytGpIPYsAC&pg=PA79}}.</ref><ref>{{citation|title=Data Structures and Algorithms in C++|first=Adam|last=Drozdek|edition=4th|publisher=Cengage Learning|date=2012|isbn=9781285415017|page=197|url=https://books.google.com/books?id=PRgLAAAAQBAJ&pg=PA197}}.</ref> |
| + | |
| | | |
| Conversely, all iterative functions and procedures that can be evaluated by a computer (see [[Turing completeness]]) can be expressed in terms of recursive functions; iterative control constructs such as [[while loop]]s and [[for loop]]s are routinely rewritten in recursive form in [[functional language]]s.<ref>{{cite web|url=http://www.ccs.neu.edu/home/shivers/papers/loop.pdf |title=The Anatomy of a Loop - A story of scope and control |publisher=Georgia Institute of Technology |first=Olin |last=Shivers |accessdate=2012-09-03}}</ref><ref>{{cite web|author=Lambda the Ultimate |url=http://lambda-the-ultimate.org/node/1014 |title=The Anatomy of a Loop |publisher=Lambda the Ultimate |accessdate=2012-09-03}}</ref> However, in practice this rewriting depends on [[tail call elimination]], which is not a feature of all languages. [[C (programming language)|C]], [[Java (programming language)|Java]], and [[Python (programming language)|Python]] are notable mainstream languages in which all function calls, including [[tail call]]s, may cause stack allocation that would not occur with the use of looping constructs; in these languages, a working iterative program rewritten in recursive form may [[stack overflow|overflow the call stack]], although tail call elimination may be a feature that is not covered by a language's specification, and different implementations of the same language may differ in tail call elimination capabilities. | | Conversely, all iterative functions and procedures that can be evaluated by a computer (see [[Turing completeness]]) can be expressed in terms of recursive functions; iterative control constructs such as [[while loop]]s and [[for loop]]s are routinely rewritten in recursive form in [[functional language]]s.<ref>{{cite web|url=http://www.ccs.neu.edu/home/shivers/papers/loop.pdf |title=The Anatomy of a Loop - A story of scope and control |publisher=Georgia Institute of Technology |first=Olin |last=Shivers |accessdate=2012-09-03}}</ref><ref>{{cite web|author=Lambda the Ultimate |url=http://lambda-the-ultimate.org/node/1014 |title=The Anatomy of a Loop |publisher=Lambda the Ultimate |accessdate=2012-09-03}}</ref> However, in practice this rewriting depends on [[tail call elimination]], which is not a feature of all languages. [[C (programming language)|C]], [[Java (programming language)|Java]], and [[Python (programming language)|Python]] are notable mainstream languages in which all function calls, including [[tail call]]s, may cause stack allocation that would not occur with the use of looping constructs; in these languages, a working iterative program rewritten in recursive form may [[stack overflow|overflow the call stack]], although tail call elimination may be a feature that is not covered by a language's specification, and different implementations of the same language may differ in tail call elimination capabilities. |
| | | |
− | 相反,所有可以被计算机评估的迭代函数和程序(见图灵完备性)都可以用递归函数来表达;在函数式语言中,诸如while循环和for循环等迭代控制构造经常以递归形式重写.然而,在实践中,这种重写依赖于尾部调用的消除,而这并不是所有语言的特征。C语言、Java和Python是值得注意的主流语言,在这些语言中,所有的函数调用(包括尾部调用)都可能会引起堆栈分配,而使用循环构造则不会出现这种情况;在这些语言中,以递归形式重写的工作迭代程序可能会溢出调用堆栈,不过尾部调用消除可能是语言规范中没有涉及的功能,同一语言的不同实现在尾部调用消除能力上可能会有所不同。
| + | 相应得,所有可以被计算机求解的迭代函数和程序(见图灵完备性)都可以用递归函数来表达;在函数式语言中,诸如while循环和for循环等迭代控制构造经常以递归形式重写<ref>{{cite web|url=http://www.ccs.neu.edu/home/shivers/papers/loop.pdf |title=The Anatomy of a Loop - A story of scope and control |publisher=Georgia Institute of Technology |first=Olin |last=Shivers |accessdate=2012-09-03}}</ref><ref>{{cite web|author=Lambda the Ultimate |url=http://lambda-the-ultimate.org/node/1014 |title=The Anatomy of a Loop |publisher=Lambda the Ultimate |accessdate=2012-09-03}}</ref>。然而,在实践中,这种重写依赖于尾部调用的消除,而这并不是所有语言的特征。C、Java和Python是值得注意的主流语言,在这些语言中,所有的函数调用(包括尾部调用)都可能会引起栈分配,而使用循环结构则不会出现这种情况;在这些语言中,以递归形式重写的迭代程序可能会造成调用栈溢出,不过尾部调用消除可能是语言规范中没有涉及的功能,同一语言的不同实现在尾部调用消除能力上可能会有所不同。 |
| + | |
| + | ===性能问题=== |
| | | |
− | ===Performance issues===
| |
− | 性能问题
| |
| In languages (such as [[C (programming language)|C]] and [[Java (programming language)|Java]]) that favor iterative looping constructs, there is usually significant time and space cost associated with recursive programs, due to the overhead required to manage the stack and the relative slowness of function calls; in [[functional languages]], a function call (particularly a [[tail call]]) is typically a very fast operation, and the difference is usually less noticeable. | | In languages (such as [[C (programming language)|C]] and [[Java (programming language)|Java]]) that favor iterative looping constructs, there is usually significant time and space cost associated with recursive programs, due to the overhead required to manage the stack and the relative slowness of function calls; in [[functional languages]], a function call (particularly a [[tail call]]) is typically a very fast operation, and the difference is usually less noticeable. |
| | | |
− | 在偏重于迭代循环结构的语言(如C语言和Java)中,由于管理堆栈所需的开销和函数调用的相对缓慢,递归程序通常会有显著的时间和空间成本;在函数式语言中,函数调用(尤其是尾部调用)通常是一个非常快的操作,这种差异通常不太明显。
| + | 在偏重于迭代循环结构的语言(如C语言和Java)中,由于管理栈所需的开销,以及函数调用的相对缓慢,递归程序通常会有显著的时间和空间成本;在函数式语言中,函数调用(尤其是尾部调用)通常是一个非常快的操作,这种差异通常不太明显。 |
| | | |
| As a concrete example, the difference in performance between recursive and iterative implementations of the "factorial" example above depends highly on the [[compiler]] used. In languages where looping constructs are preferred, the iterative version may be as much as several [[order of magnitude|orders of magnitude]] faster than the recursive one. In functional languages, the overall time difference of the two implementations may be negligible; in fact, the cost of multiplying the larger numbers first rather than the smaller numbers (which the iterative version given here happens to do) may overwhelm any time saved by choosing iteration. | | As a concrete example, the difference in performance between recursive and iterative implementations of the "factorial" example above depends highly on the [[compiler]] used. In languages where looping constructs are preferred, the iterative version may be as much as several [[order of magnitude|orders of magnitude]] faster than the recursive one. In functional languages, the overall time difference of the two implementations may be negligible; in fact, the cost of multiplying the larger numbers first rather than the smaller numbers (which the iterative version given here happens to do) may overwhelm any time saved by choosing iteration. |
| | | |
− | 作为一个具体的例子,上述 "函数 "例子的递归和迭代实现之间的性能差异很大程度上取决于所使用的编译器。在偏好循环结构的语言中,迭代版本可能比递归版本快几个数量级。在函数式语言中,两种实现的总体时间差异可能可以忽略不计;事实上,先乘大数而不是先乘小数的成本(这里给出的迭代版本恰好做到了这一点)可能会超过选择迭代所节省的任何时间。 | + | 作为一个具体的例子,上述 "阶乘 "例子的递归和迭代实现之间的性能差异很大程度上取决于所使用的编译器。在偏好循环结构的语言中,迭代版本可能比递归版本快几个数量级。在函数式语言中,两种实现的总体时间差异可能可以忽略不计;事实上,先乘大数而不是先乘小数的成本(这里给出的迭代版本就是这么做的)可能会超过选择迭代所节省得任何时间。 |
| + | |
| + | ===栈空间=== |
| | | |
− | ===Stack space===
| |
− | 堆栈空间
| |
| In some programming languages, the maximum size of the [[call stack]] is much less than the space available in the [[Heap (programming)|heap]], and recursive algorithms tend to require more stack space than iterative algorithms. Consequently, these languages sometimes place a limit on the depth of recursion to avoid [[stack buffer overflow|stack overflow]]s; [[python (programming language)|Python]] is one such language.<ref>{{cite web|url=https://docs.python.org/library/sys.html |title=27.1. sys — System-specific parameters and functions — Python v2.7.3 documentation |publisher=Docs.python.org |accessdate=2012-09-03}}</ref> Note the caveat below regarding the special case of [[tail recursion]]. | | In some programming languages, the maximum size of the [[call stack]] is much less than the space available in the [[Heap (programming)|heap]], and recursive algorithms tend to require more stack space than iterative algorithms. Consequently, these languages sometimes place a limit on the depth of recursion to avoid [[stack buffer overflow|stack overflow]]s; [[python (programming language)|Python]] is one such language.<ref>{{cite web|url=https://docs.python.org/library/sys.html |title=27.1. sys — System-specific parameters and functions — Python v2.7.3 documentation |publisher=Docs.python.org |accessdate=2012-09-03}}</ref> Note the caveat below regarding the special case of [[tail recursion]]. |
| | | |
− | 在一些编程语言中,调用栈的最大大小远小于堆中的可用空间,递归算法往往比迭代算法需要更多的栈空间。因此,这些语言有时会对递归的深度进行限制,以避免堆栈溢出;Python就是这样一种语言。注意下面关于尾部递归的特殊情况的注意事项。
| + | 在一些编程语言中,调用栈的最大规模远小于堆中的可用空间,而递归算法往往比迭代算法需要更多的栈空间。因此,这些语言有时会对递归的深度进行限制,以避免栈溢出;Python就是这样一种语言<ref>{{cite web|url=https://docs.python.org/library/sys.html |title=27.1. sys — System-specific parameters and functions — Python v2.7.3 documentation |publisher=Docs.python.org |accessdate=2012-09-03}}</ref>。注意下面关于尾部递归的特殊情况的注意事项。 |
| + | |
| + | ===漏洞=== |
| | | |
− | ===Vulnerability===
| |
− | 漏洞
| |
| Because recursive algorithms can be subject to stack overflows, they may be vulnerable to [[pathological (mathematics)|pathological]] or [[malware|malicious]] input.<ref>{{cite magazine| last=Krauss| first=Kirk J.| title=Matching Wildcards: An Empirical Way to Tame an Algorithm| magazine=[[Dr. Dobb's Journal]]| date=2014| url=http://www.drdobbs.com/architecture-and-design/matching-wildcards-an-empirical-way-to-t/240169123}}</ref> Some malware specifically targets a program's call stack and takes advantage of the stack's inherently recursive nature.<ref>{{cite magazine| last=Mueller| first=Oliver| title=Anatomy of a Stack Smashing Attack and How GCC Prevents It| magazine=[[Dr. Dobb's Journal]]| date=2012| url=http://www.drdobbs.com/security/anatomy-of-a-stack-smashing-attack-and-h/240001832}}</ref> Even in the absence of malware, a stack overflow caused by unbounded recursion can be fatal to the program, and [[exception handling]] [[logic]] may not prevent the corresponding [[process (computing)|process]] from being [[process state#Terminated|terminated]].<ref>{{cite web| work=.NET Framework Class Library| title=StackOverflowException Class| publisher=[[Microsoft Developer Network]]| date=2018| url=https://msdn.microsoft.com/en-us/library/system.stackoverflowexception(v=vs.110).aspx}}</ref> | | Because recursive algorithms can be subject to stack overflows, they may be vulnerable to [[pathological (mathematics)|pathological]] or [[malware|malicious]] input.<ref>{{cite magazine| last=Krauss| first=Kirk J.| title=Matching Wildcards: An Empirical Way to Tame an Algorithm| magazine=[[Dr. Dobb's Journal]]| date=2014| url=http://www.drdobbs.com/architecture-and-design/matching-wildcards-an-empirical-way-to-t/240169123}}</ref> Some malware specifically targets a program's call stack and takes advantage of the stack's inherently recursive nature.<ref>{{cite magazine| last=Mueller| first=Oliver| title=Anatomy of a Stack Smashing Attack and How GCC Prevents It| magazine=[[Dr. Dobb's Journal]]| date=2012| url=http://www.drdobbs.com/security/anatomy-of-a-stack-smashing-attack-and-h/240001832}}</ref> Even in the absence of malware, a stack overflow caused by unbounded recursion can be fatal to the program, and [[exception handling]] [[logic]] may not prevent the corresponding [[process (computing)|process]] from being [[process state#Terminated|terminated]].<ref>{{cite web| work=.NET Framework Class Library| title=StackOverflowException Class| publisher=[[Microsoft Developer Network]]| date=2018| url=https://msdn.microsoft.com/en-us/library/system.stackoverflowexception(v=vs.110).aspx}}</ref> |
| | | |
− | 由于递归算法可能会发生堆栈溢出,因此它们可能容易受到病态或恶意输入的影响.一些恶意软件专门针对程序的调用堆栈,并利用堆栈固有的递归特性。即使在没有恶意软件的情况下,由无约束递归引起的堆栈溢出也会对程序造成致命的影响,而异常处理逻辑可能无法阻止相应进程被终止。
| + | 由于递归算法可能会引发栈溢出,因此它们可能容易受到异常或恶意输入的影响<ref>{{cite magazine| last=Krauss| first=Kirk J.| title=Matching Wildcards: An Empirical Way to Tame an Algorithm| magazine=[[Dr. Dobb's Journal]]| date=2014| url=http://www.drdobbs.com/architecture-and-design/matching-wildcards-an-empirical-way-to-t/240169123}}</ref>。一些恶意软件专门针对程序的调用栈,并利用栈固有的递归特性<ref>{{cite magazine| last=Mueller| first=Oliver| title=Anatomy of a Stack Smashing Attack and How GCC Prevents It| magazine=[[Dr. Dobb's Journal]]| date=2012| url=http://www.drdobbs.com/security/anatomy-of-a-stack-smashing-attack-and-h/240001832}}</ref> 。即使在没有恶意软件的情况下,由无约束递归引起的栈溢出也会对程序造成致命的影响,而异常处理逻辑可能无法阻止相应进程被终止<ref>{{cite web| work=.NET Framework Class Library| title=StackOverflowException Class| publisher=[[Microsoft Developer Network]]| date=2018| url=https://msdn.microsoft.com/en-us/library/system.stackoverflowexception(v=vs.110).aspx}}</ref>。 |
| + | |
| + | |
| + | ===多重递归问题=== |
| | | |
− | ===Multiply recursive problems===
| |
− | 多次递归问题
| |
| Multiply recursive problems are inherently recursive, because of prior state they need to track. One example is [[tree traversal]] as in [[depth-first search]]; though both recursive and iterative methods are used,<ref>{{cite web| title=Depth First Search (DFS): Iterative and Recursive Implementation| publisher=Techie Delight| date=2018| url=http://www.techiedelight.com/depth-first-search/}}</ref> they contrast with list traversal and linear search in a list, which is a singly recursive and thus naturally iterative method. Other examples include [[divide-and-conquer algorithm]]s such as [[Quicksort]], and functions such as the [[Ackermann function]]. All of these algorithms can be implemented iteratively with the help of an explicit [[stack (data structure)|stack]], but the programmer effort involved in managing the stack, and the complexity of the resulting program, arguably outweigh any advantages of the iterative solution. | | Multiply recursive problems are inherently recursive, because of prior state they need to track. One example is [[tree traversal]] as in [[depth-first search]]; though both recursive and iterative methods are used,<ref>{{cite web| title=Depth First Search (DFS): Iterative and Recursive Implementation| publisher=Techie Delight| date=2018| url=http://www.techiedelight.com/depth-first-search/}}</ref> they contrast with list traversal and linear search in a list, which is a singly recursive and thus naturally iterative method. Other examples include [[divide-and-conquer algorithm]]s such as [[Quicksort]], and functions such as the [[Ackermann function]]. All of these algorithms can be implemented iteratively with the help of an explicit [[stack (data structure)|stack]], but the programmer effort involved in managing the stack, and the complexity of the resulting program, arguably outweigh any advantages of the iterative solution. |
| | | |
− | 多次递归问题本质上是递归的,因为它们需要跟踪之前的状态。一个例子是深度优先搜索中的树形遍历;虽然同时使用了递归和迭代方法,但它们与单次递归的列表遍历和列表中的线性搜索相比,后者是一种自然的迭代方法。其他例子包括分治算法(如快速排序)和函数(如Ackermann函数)。所有这些算法都可以在显式栈的帮助下迭代地实现,但是程序员在管理栈方面所付出的努力,以及结果程序的复杂性,无疑超过了迭代解决方案的任何优势。
| + | 多重递归问题本质上是递归的,因为它们需要跟踪之前的状态。一个例子是深度优先搜索中的树遍历。虽然可以同时使用了递归和迭代方法<ref>{{cite web| title=Depth First Search (DFS): Iterative and Recursive Implementation| publisher=Techie Delight| date=2018| url=http://www.techiedelight.com/depth-first-search/}}</ref>,但它们与单重递归的列表遍历和列表中的线性搜索相比,后者是一种自然的迭代方法。其他例子包括分治算法(如快速排序)和函数(如Ackermann函数)。所有这些算法都可以在显式栈的帮助下迭代地实现,但是程序员在管理栈方面所付出的努力,以及结果程序的复杂性,无疑超过了迭代解决方案的任何优势。 |
| + | |
| + | |
| + | ===重构递归=== |
| | | |
− | ===Refactoring recursion===
| |
− | 重构递归
| |
| Recursive algorithms can be replaced with non-recursive counterparts.<ref>{{cite web| last=Mitrovic| first=Ivan| title=Replace Recursion with Iteration| publisher=[[ThoughtWorks]]| url=https://www.refactoring.com/catalog/replaceRecursionWithIteration.html}}</ref> One method for replacing recursive algorithms is to simulate them using [[memory management|heap memory]] in place of [[stack-based memory allocation|stack memory]].<ref>{{cite web| last=La| first=Woong Gyu| title=How to replace recursive functions using stack and while-loop to avoid the stack-overflow| publisher=CodeProject| date=2015| url=https://www.codeproject.com/Articles/418776/How-to-replace-recursive-functions-using-stack-and}}</ref> An alternative is to develop a replacement algorithm entirely based on non-recursive methods, which can be challenging.<ref>{{cite web| last=Moertel| first=Tom| title=Tricks of the trade: Recursion to Iteration, Part 2: Eliminating Recursion with the Time-Traveling Secret Feature Trick| date=2013| url=http://blog.moertel.com/posts/2013-05-14-recursive-to-iterative-2.html}}</ref> For example, recursive algorithms for [[matching wildcards]], such as [[Rich Salz]]' [[wildmat]] algorithm,<ref>{{cite web| last=Salz| first=Rich| title=wildmat.c| publisher=[[GitHub]]| date=1991| url=https://github.com/trevor/transmission/blob/master/libtransmission/wildmat.c}}</ref> were once typical. Non-recursive algorithms for the same purpose, such as the [[Krauss matching wildcards algorithm]], have been developed to avoid the drawbacks of recursion<ref>{{cite magazine| last=Krauss| first=Kirk J.| title=Matching Wildcards: An Algorithm| magazine=[[Dr. Dobb's Journal]]| date=2008| url=http://www.drdobbs.com/architecture-and-design/matching-wildcards-an-algorithm/210200888}}</ref> and have improved only gradually based on techniques such as collecting [[software testing|tests]] and [[profiling (computer programming)|profiling]] performance.<ref>{{cite web| last=Krauss| first=Kirk J.| title=Matching Wildcards: An Improved Algorithm for Big Data| publisher=Develop for Performance| date=2018| url=http://www.developforperformance.com/MatchingWildcards_AnImprovedAlgorithmForBigData.html}}</ref> | | Recursive algorithms can be replaced with non-recursive counterparts.<ref>{{cite web| last=Mitrovic| first=Ivan| title=Replace Recursion with Iteration| publisher=[[ThoughtWorks]]| url=https://www.refactoring.com/catalog/replaceRecursionWithIteration.html}}</ref> One method for replacing recursive algorithms is to simulate them using [[memory management|heap memory]] in place of [[stack-based memory allocation|stack memory]].<ref>{{cite web| last=La| first=Woong Gyu| title=How to replace recursive functions using stack and while-loop to avoid the stack-overflow| publisher=CodeProject| date=2015| url=https://www.codeproject.com/Articles/418776/How-to-replace-recursive-functions-using-stack-and}}</ref> An alternative is to develop a replacement algorithm entirely based on non-recursive methods, which can be challenging.<ref>{{cite web| last=Moertel| first=Tom| title=Tricks of the trade: Recursion to Iteration, Part 2: Eliminating Recursion with the Time-Traveling Secret Feature Trick| date=2013| url=http://blog.moertel.com/posts/2013-05-14-recursive-to-iterative-2.html}}</ref> For example, recursive algorithms for [[matching wildcards]], such as [[Rich Salz]]' [[wildmat]] algorithm,<ref>{{cite web| last=Salz| first=Rich| title=wildmat.c| publisher=[[GitHub]]| date=1991| url=https://github.com/trevor/transmission/blob/master/libtransmission/wildmat.c}}</ref> were once typical. Non-recursive algorithms for the same purpose, such as the [[Krauss matching wildcards algorithm]], have been developed to avoid the drawbacks of recursion<ref>{{cite magazine| last=Krauss| first=Kirk J.| title=Matching Wildcards: An Algorithm| magazine=[[Dr. Dobb's Journal]]| date=2008| url=http://www.drdobbs.com/architecture-and-design/matching-wildcards-an-algorithm/210200888}}</ref> and have improved only gradually based on techniques such as collecting [[software testing|tests]] and [[profiling (computer programming)|profiling]] performance.<ref>{{cite web| last=Krauss| first=Kirk J.| title=Matching Wildcards: An Improved Algorithm for Big Data| publisher=Develop for Performance| date=2018| url=http://www.developforperformance.com/MatchingWildcards_AnImprovedAlgorithmForBigData.html}}</ref> |
| | | |
− | 递归算法可以用非递归的对应算法来替换。替换递归算法的一种方法是用堆内存代替栈内存来模拟它们。另一种方法是完全基于非递归方法来开发替换算法,这可能是一个挑战。例如,用于匹配通配符的递归算法,如Rich Salz的wildmat算法,曾经是典型的算法。为了避免递归的缺点,人们开发了用于相同目的的非递归算法,如Krauss匹配通配符算法,并在收集测试和性能分析等技术的基础上逐步改进。
| + | 递归算法可以用非递归的对应算法来替换<ref>{{cite web| last=Mitrovic| first=Ivan| title=Replace Recursion with Iteration| publisher=[[ThoughtWorks]]| url=https://www.refactoring.com/catalog/replaceRecursionWithIteration.html}}</ref>。替换递归算法的一种方法是用堆内存代替栈内存来模拟它们<ref>{{cite web| last=La| first=Woong Gyu| title=How to replace recursive functions using stack and while-loop to avoid the stack-overflow| publisher=CodeProject| date=2015| url=https://www.codeproject.com/Articles/418776/How-to-replace-recursive-functions-using-stack-and}}</ref> 。另一种方法是完全基于非递归方法来开发替换算法,这可能是一个挑战<ref>{{cite web| last=Moertel| first=Tom| title=Tricks of the trade: Recursion to Iteration, Part 2: Eliminating Recursion with the Time-Traveling Secret Feature Trick| date=2013| url=http://blog.moertel.com/posts/2013-05-14-recursive-to-iterative-2.html}}</ref> 。例如,用于通配符匹配的递归算法,如Rich Salz的wildmat算法<ref>{{cite web| last=Salz| first=Rich| title=wildmat.c| publisher=[[GitHub]]| date=1991| url=https://github.com/trevor/transmission/blob/master/libtransmission/wildmat.c}}</ref>,曾经是典型的算法。为了避免递归的缺点<ref>{{cite magazine| last=Krauss| first=Kirk J.| title=Matching Wildcards: An Algorithm| magazine=[[Dr. Dobb's Journal]]| date=2008| url=http://www.drdobbs.com/architecture-and-design/matching-wildcards-an-algorithm/210200888}}</ref>,人们开发了用于相同目的的非递归算法,如Krauss通配符匹配算法,并在收集测试和性能分析等技术的基础上逐步改进。<ref>{{cite web| last=Krauss| first=Kirk J.| title=Matching Wildcards: An Improved Algorithm for Big Data| publisher=Develop for Performance| date=2018| url=http://www.developforperformance.com/MatchingWildcards_AnImprovedAlgorithmForBigData.html}}</ref> |
| | | |
| ==Tail-recursive functions== | | ==Tail-recursive functions== |