I've recently seen in a couple of different places comments along the lines of, "I learned about recursion in school, but have never used it or felt the need for it since then." (Recursion seems to be a popular example of "book learning" amongst a certain group of programmers.)
我最近在几个不同的地方看到过这样的评论:“我在学校学习了递归,但从那时起就没有使用它或感觉到需要它。” (递归似乎是某一类程序员中“书本学习”的一个流行的例子。)
Well, it's true that in imperative languages such as Java and Ruby[1], we generally use iteration and avoid recursion, in part because of the risk of stack overflows, and in part because it's the style most programmers in those languages are used to.
嗯,在Java和Ruby [1]等命令式语言中,我们通常使用迭代并避免递归,部分原因是存在堆栈溢出的风险,部分原因是因为这些语言中的大多数程序员都习惯使用。
Now I know that, strictly speaking, there are no "necessary" uses of recursion in such languages: one can always somehow replace recursion with iteration, no matter how complex things get. By "necessary" here, I'm talking about the following:
现在我知道,严格来说,在这样的语言中没有“必要的”递归用法:无论事情多么复杂,人们都可以用迭代取代递归。在这里“必要”,我说的是以下内容:
Can you think of any particular examples of code in such languages where recursion was so much better than iteration (for reasons of clarity, efficiency, or otherwise) that you used recursion anyway, and converting to iteration would have been a big loss?
您是否可以想到这些语言中代码的任何特定示例,其中递归比迭代(为了清晰,效率或其他原因)更好,无论如何您使用递归,转换为迭代会是一个很大的损失?
Recursively walking trees has been mentioned several times in the answers: what was it exactly about your particular use of it that made recursion better than using a library-defined iterator, had it been available?
在答案中已经多次提到了递归行走的树:如果它可用,那么它使用它的确切原因是什么使得递归比使用库定义的迭代器更好?
[1]: Yes, I know that these are also object-oriented languages. That's not directly relevant to this question, however.
[1]:是的,我知道这些也是面向对象的语言。然而,这与这个问题没有直接关系。
9 个解决方案
#1
There are no "necessary" uses of recursion. All recursive algorithms can be converted to iterative ones. I seem to recall a stack being necessary, but I can't recall the exact construction off the top of my head.
递归没有“必要的”用法。所有递归算法都可以转换为迭代算法。我似乎记得堆栈是必要的,但我不记得我头顶的确切结构。
Practically speaking, if you're not using recursion for the following (even in imperative languages) you're a little mad:
实际上,如果你没有使用递归来表示以下内容(即使是在命令式语言中),你会有点生气:
- Tree traversal
- Graphs
- Lexing/Parsing
- Sorting
#2
When you are walking any kind of tree structure, for example
例如,当您走任何类型的树结构时
- parsing a grammar using a recursive-descent parser
- walking a DOM tree (e.g. parsed HTML or XML)
使用递归下降解析器解析语法
走DOM树(例如解析的HTML或XML)
also, every toString() method that calls the toString() of the object members can be considered recursive, too. All object serializing algorithms are recursive.
另外,每个调用对象成员的toString()的toString()方法也可以被认为是递归的。所有对象序列化算法都是递归的。
#3
In my work recursion is very rarely used for anything algorithmic. Things like factorials etc are solved much more readably (and efficiently) using simple loops. When it does show up it is usually because you are processing some data that is recursive in nature. For example, the nodes on a tree structure could be processed recursively.
在我的工作中,递归很少用于任何算法。使用简单的循环可以更加可读地(并且有效地)解决诸如阶乘等问题。当它出现时,通常是因为您正在处理一些本质上递归的数据。例如,可以递归地处理树结构上的节点。
If you were to write a program to walk the nodes of a binary tree for example, you could write a function that processed one node, and called itself to process each of it's children. This would be more effective than trying to maintain all the different states for each child node as you looped through them.
例如,如果您要编写一个程序来遍历二叉树的节点,您可以编写一个处理一个节点的函数,并调用它来处理每个节点的子节点。这比在环绕每个子节点时尝试维护所有不同状态更有效。
#4
The most well-known example is probably the quicksort algorithm developed by by C.A.R. Hoare.
最着名的例子可能是C.A.R.开发的快速排序算法。霍尔。
Another example is traversing a directory tree for finding a file.
另一个例子是遍历目录树以查找文件。
#5
In my opinion, recursive algorithms are a natural fit when the data structure is also recursive.
在我看来,当数据结构也是递归的时候,递归算法是很自然的。
def traverse(node, function):
function(this)
for each childnode in children:
traverse(childnode, function)
I can't see why I'd want to write that iteratively.
我不明白为什么我想要迭代地写这个。
#6
It's all about the data you are processing.
这都与您正在处理的数据有关。
I wrote a simple parser to convert a string into a data structure, it's probably the only example in 5 years' work in Java, but I think it was the right way to do it.
我写了一个简单的解析器来将字符串转换为数据结构,它可能是5年来Java中唯一的例子,但我认为这是正确的方法。
The string looked like this:
字符串看起来像这样:
"{ index = 1, ID = ['A', 'B', 'C'], data = {" +
"count = 112, flags = FLAG_1 | FLAG_2 }}"
The best abstraction for this was a tree, where all leaf nodes are primitive data types, and branches could be arrays or objects. This is the typical recursive problem, a non-recursive solution is possible but much more complex.
对此最好的抽象是树,其中所有叶节点都是原始数据类型,分支可以是数组或对象。这是典型的递归问题,非递归解决方案是可能的,但更复杂。
#7
Recursion can always be rewritten as iteration with an external stack. However if you're sure that you don't risk very deep recursion that would lead to *, recursion is a very convenient thing.
可以使用外部堆栈将递归重写为迭代。但是,如果您确定不会冒很大的递归风险导致*,那么递归是一件非常方便的事情。
One good example is traversing a directory structure on a known operating system. You usually know how deep it can be (maximum path length is limited) and therefore will not have a *. Doing the same via iteration with an external stack is not so convenient.
一个很好的例子是遍历已知操作系统上的目录结构。您通常知道它有多深(最大路径长度有限),因此不会有堆栈溢出。通过使用外部堆栈进行迭代来做同样的事情并不方便。
#8
It was said "anything tree". I may be too cautious, and I know that stacks are big nowadays, but I still won't use recursion on a typical tree. I would, however, do it on a balanced tree.
有人说“什么树”。我可能太谨慎了,我知道现在堆栈很大,但我仍然不会在典型的树上使用递归。但是,我会在平衡的树上做到这一点。
#9
I have a List of reports. I am using indexers on my class that contains this list. The reports are retrieved by their screen names using the indexers. In the indexer, if the report for that screen name doesn't exist it loads the report and recursively calls itself.
我有一份报告清单。我在我的类中使用包含此列表的索引器。使用索引器通过屏幕名称检索报告。在索引器中,如果该屏幕名称的报告不存在,则会加载报告并以递归方式调用自身。
public class ReportDictionary
{
private static List<Report> _reportList = null;
public ReportColumnList this[string screenName]
{
get
{
Report rc = _reportList.Find(delegate(Report obj) { return obj.ReportName == screenName; });
if (rc == null)
{
this.Load(screenName);
return this[screenName]; // Recursive call
}
else
return rc.ReportColumnList.Copy();
}
private set
{
this.Add(screenName, value);
}
}
}
This can be done without recursion using some additional lines of code.
这可以使用一些额外的代码行在没有递归的情况下完成。
#1
There are no "necessary" uses of recursion. All recursive algorithms can be converted to iterative ones. I seem to recall a stack being necessary, but I can't recall the exact construction off the top of my head.
递归没有“必要的”用法。所有递归算法都可以转换为迭代算法。我似乎记得堆栈是必要的,但我不记得我头顶的确切结构。
Practically speaking, if you're not using recursion for the following (even in imperative languages) you're a little mad:
实际上,如果你没有使用递归来表示以下内容(即使是在命令式语言中),你会有点生气:
- Tree traversal
- Graphs
- Lexing/Parsing
- Sorting
#2
When you are walking any kind of tree structure, for example
例如,当您走任何类型的树结构时
- parsing a grammar using a recursive-descent parser
- walking a DOM tree (e.g. parsed HTML or XML)
使用递归下降解析器解析语法
走DOM树(例如解析的HTML或XML)
also, every toString() method that calls the toString() of the object members can be considered recursive, too. All object serializing algorithms are recursive.
另外,每个调用对象成员的toString()的toString()方法也可以被认为是递归的。所有对象序列化算法都是递归的。
#3
In my work recursion is very rarely used for anything algorithmic. Things like factorials etc are solved much more readably (and efficiently) using simple loops. When it does show up it is usually because you are processing some data that is recursive in nature. For example, the nodes on a tree structure could be processed recursively.
在我的工作中,递归很少用于任何算法。使用简单的循环可以更加可读地(并且有效地)解决诸如阶乘等问题。当它出现时,通常是因为您正在处理一些本质上递归的数据。例如,可以递归地处理树结构上的节点。
If you were to write a program to walk the nodes of a binary tree for example, you could write a function that processed one node, and called itself to process each of it's children. This would be more effective than trying to maintain all the different states for each child node as you looped through them.
例如,如果您要编写一个程序来遍历二叉树的节点,您可以编写一个处理一个节点的函数,并调用它来处理每个节点的子节点。这比在环绕每个子节点时尝试维护所有不同状态更有效。
#4
The most well-known example is probably the quicksort algorithm developed by by C.A.R. Hoare.
最着名的例子可能是C.A.R.开发的快速排序算法。霍尔。
Another example is traversing a directory tree for finding a file.
另一个例子是遍历目录树以查找文件。
#5
In my opinion, recursive algorithms are a natural fit when the data structure is also recursive.
在我看来,当数据结构也是递归的时候,递归算法是很自然的。
def traverse(node, function):
function(this)
for each childnode in children:
traverse(childnode, function)
I can't see why I'd want to write that iteratively.
我不明白为什么我想要迭代地写这个。
#6
It's all about the data you are processing.
这都与您正在处理的数据有关。
I wrote a simple parser to convert a string into a data structure, it's probably the only example in 5 years' work in Java, but I think it was the right way to do it.
我写了一个简单的解析器来将字符串转换为数据结构,它可能是5年来Java中唯一的例子,但我认为这是正确的方法。
The string looked like this:
字符串看起来像这样:
"{ index = 1, ID = ['A', 'B', 'C'], data = {" +
"count = 112, flags = FLAG_1 | FLAG_2 }}"
The best abstraction for this was a tree, where all leaf nodes are primitive data types, and branches could be arrays or objects. This is the typical recursive problem, a non-recursive solution is possible but much more complex.
对此最好的抽象是树,其中所有叶节点都是原始数据类型,分支可以是数组或对象。这是典型的递归问题,非递归解决方案是可能的,但更复杂。
#7
Recursion can always be rewritten as iteration with an external stack. However if you're sure that you don't risk very deep recursion that would lead to *, recursion is a very convenient thing.
可以使用外部堆栈将递归重写为迭代。但是,如果您确定不会冒很大的递归风险导致*,那么递归是一件非常方便的事情。
One good example is traversing a directory structure on a known operating system. You usually know how deep it can be (maximum path length is limited) and therefore will not have a *. Doing the same via iteration with an external stack is not so convenient.
一个很好的例子是遍历已知操作系统上的目录结构。您通常知道它有多深(最大路径长度有限),因此不会有堆栈溢出。通过使用外部堆栈进行迭代来做同样的事情并不方便。
#8
It was said "anything tree". I may be too cautious, and I know that stacks are big nowadays, but I still won't use recursion on a typical tree. I would, however, do it on a balanced tree.
有人说“什么树”。我可能太谨慎了,我知道现在堆栈很大,但我仍然不会在典型的树上使用递归。但是,我会在平衡的树上做到这一点。
#9
I have a List of reports. I am using indexers on my class that contains this list. The reports are retrieved by their screen names using the indexers. In the indexer, if the report for that screen name doesn't exist it loads the report and recursively calls itself.
我有一份报告清单。我在我的类中使用包含此列表的索引器。使用索引器通过屏幕名称检索报告。在索引器中,如果该屏幕名称的报告不存在,则会加载报告并以递归方式调用自身。
public class ReportDictionary
{
private static List<Report> _reportList = null;
public ReportColumnList this[string screenName]
{
get
{
Report rc = _reportList.Find(delegate(Report obj) { return obj.ReportName == screenName; });
if (rc == null)
{
this.Load(screenName);
return this[screenName]; // Recursive call
}
else
return rc.ReportColumnList.Copy();
}
private set
{
this.Add(screenName, value);
}
}
}
This can be done without recursion using some additional lines of code.
这可以使用一些额外的代码行在没有递归的情况下完成。