循环和递归之间的实际区别是什么
我目前正在使用 PHP 工作,因此这个示例将使用 PHP,但问题适用于多种语言。
我正在和我的一个朋友一起做这个项目,和往常一样,我们遇到了一个大问题。现在我们俩都回家了,解决不了问题。那天晚上我们都找到了解决办法,只是我用了循环来解决问题,而他用了递归。
现在我想告诉他循环和递归之间的区别,但我无法想出一个在正常循环上需要递归的解决方案。
我将制作两者的简化版本,我希望有人能够解释其中一个与另一个的不同之处。
请原谅我的任何编码错误
循环:
printnumbers(1,10);
public function printnumbers($start,$stop)
{
for($i=$start;$i<=$stop;$i++)
{
echo $i;
}
}
现在上面的代码只是简单地打印出数字。
现在让我们使用递归来完成此操作:
printnumbers(1,10);
public function printnumbers($start,$stop)
{
$i = $start;
if($i <= $stop)
{
echo $i;
printnumbers($start+1,$stop);
}
}
上面的方法将执行与循环完全相同的操作,但仅使用递归。
谁能向我解释一下使用其中一种方法有什么不同。
I am currently working in PHP, so this example will be in PHP, but the question applies to multiple languages.
I am working on this project with a fiend of mine, and as always we were held up by a big problem. Now we both went home, couldn't solve the problem. That night we both found the solution, only I used a loop to tackle the problem, and he used recursion.
Now I wanted to tell him the difference between the loop and recursion, but I couldn't come up with a solution where you need recursion over a normal loop.
I am going to make a simplified version of both, I hope someone can explain how one is different from the other.
Please forgive me for any coding errors
The loop:
printnumbers(1,10);
public function printnumbers($start,$stop)
{
for($i=$start;$i<=$stop;$i++)
{
echo $i;
}
}
Now the code above just simply prints out the numbers.
Now let's do this with recursion:
printnumbers(1,10);
public function printnumbers($start,$stop)
{
$i = $start;
if($i <= $stop)
{
echo $i;
printnumbers($start+1,$stop);
}
}
This method above will do the exact same thing as the loop, but then only with recursion.
Can anyone explain to me what there is different about using one of these methods.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
循环和递归在很多方面是等效的。没有程序需要其中之一,原则上你总是可以从循环转换为递归,反之亦然。
递归更强大,因为将递归转换为循环可能需要一个您必须自己操作的堆栈。 (尝试使用循环遍历二叉树,您会感到痛苦。)
另一方面,许多语言(和实现),例如 Java,没有正确实现尾递归。尾递归是指您在函数中做的最后一件事是调用自己(如您的示例中所示)。这种递归不必消耗任何堆栈,但在许多语言中它们会消耗任何堆栈,这意味着您不能总是使用递归。
Loops and recursions are in many ways equivalent. There are no programs the need one or the other, in principle you can always translate from loops to recursion or vice versa.
Recursions is more powerful in the sense that to translating recursion to a loop might need a stack that you have to manipulate yourself. (Try traversing a binary tree using a loop and you will feel the pain.)
On the other hand, many languages (and implementations), e.g., Java, don't implement tail recursion properly. Tail recursion is when the last thing you do in a function is to call yourself (like in your example). This kind of recursion does not have to consume any stack, but in many languages they do, which means you can't always use recursion.
通常,使用递归更容易表达问题。当您谈论树状数据结构(例如目录、决策树...)时尤其如此。
这些数据结构本质上是有限的,因此大多数时候通过递归处理它们会更清晰。
当堆栈深度通常是有限的,并且每个函数调用都需要一块堆栈时,当谈论可能无限的数据结构时,您将不得不放弃递归并将其转换为迭代。
尤其是函数式语言擅长处理“无限”递归。命令式语言专注于类似迭代的循环。
Often, a problem is easier expressed using recursion. This is especially true when you talk about tree-like data structures (e.g. directories, decision trees...).
These data structures are finite in nature, so most of the time processing them is clearer with recursion.
When stack-depth is often limited, and every function call requires a piece of stack, and when talking about a possibly infinite data structure you will have to abandon recursion and translate it into iteration.
Especially functional languages are good at handling 'infinite' recursion. Imperative languages are focused on iteration-like loops.
一般来说,递归函数会消耗更多的堆栈空间(因为它实际上是一大堆函数调用),而迭代解决方案则不会。这也意味着迭代解决方案通常会更快,因为。
我不确定这是否适用于像 PHP 这样的解释语言,但解释器可能可以更好地处理这个问题。
In general, a recursive function will consume more stack space (since it's really a large set of function calls), while an iterative solution won't. This also means that an iterative solution, in general, will be faster because.
I am not sure if this applies to an interpreted language like PHP though, it is possible that the interpreter can handle this better.
循环会更快,因为执行额外的函数调用总是有开销。
学习递归的一个问题是,给出的许多示例(例如阶乘)都是使用递归的糟糕示例。
如果可能,请坚持使用循环,除非您需要做不同的事情。使用递归的一个很好的例子是循环遍历具有多个子节点级别的树中的每个节点。
A loop will be faster because there's always overhead in executing an extra function call.
A problem with learning about recursion is a lot of the examples given (say, factorials) are bad examples of using recursion.
Where possible, stick with a loop unless you need to do something different. A good example of using recursion is looping over each node in a Tree with multiple levels of child nodes.
递归有点慢(因为函数调用比设置变量慢),并且在大多数语言的调用堆栈上使用更多空间。如果您尝试
printnumbers(1, 1000000000)
,递归版本可能会抛出 PHP 致命错误,甚至 500 错误。在某些情况下,递归是有意义的,例如对树的每个部分执行某些操作(获取目录及其子目录中的所有文件,或者可能弄乱 XML 文档),但它有其代价——速度、堆栈占用空间,以及确保它不会一遍又一遍地调用自己直到崩溃所花费的时间。如果循环更有意义,那么它绝对是正确的选择。
Recursion is a bit slower (because function calls are slower than setting a variable), and uses more space on most languages' call stacks. If you tried to
printnumbers(1, 1000000000)
, the recursive version would likely throw a PHP fatal error or even a 500 error.There are some cases where recursion makes sense, like doing something to every part of a tree (getting all files in a directory and its subdirectories, or maybe messing with an XML document), but it has its price -- in speed, stack footprint, and the time spent to make sure it doesn't get stuck calling itself over and over til it crashes. If a loop makes more sense, it's definitely the way to go.
好吧,我不了解 PHP,但大多数语言都会为每次递归生成函数调用(在机器级别)。因此,它们有可能使用大量堆栈空间,除非编译器产生尾部调用优化(如果您的代码允许)。
从这个意义上说,循环更“高效”,因为它们不会增加堆栈。不过,递归的优点是能够更自然地表达某些任务。
在这个具体案例中,从概念(而不是实现)的角度来看,这两种解决方案是完全等效的。
Well, I don't know about PHP but most languages generate a function call (at the machine level) for every recursion. So they have the potential to use a lot of stack space, unless the compiler produces tail-call optimizations (if your code allows it).
Loops are more 'efficient' in that sense because they don't grow the stack. Recursion has the advantage of being able to express some tasks more naturally though.
In this specific case, from a conceptual (rather than implementative) point of view, the two solutions are totally equivalent.
与循环相比,函数调用有其自己的开销,例如分配堆栈等。在大多数情况下,循环比递归对应物更容易理解。
此外,如果启动和停止之间的差异很大并且同时运行此代码的实例太多(当您获得更多流量时可能会发生这种情况),您最终将使用更多内存,甚至可能耗尽堆栈空间。
Compared to loops, a function call has its own overhead like allocating stack etc. And in most cases, loops are more understandable than their recursive counterparts.
Also, you will end up using more memory and can even run out of stack space if the difference between start and stop is high and there are too many instances of this code running simultaneously (which can happen as you get more traffic).
对于这样的平面结构,您实际上并不需要递归。我使用递归的第一个代码涉及管理物理容器。每个容器可能包含一些东西(它们的列表,每个容器都有重量)和/或更多容器,每个容器都有一个重量。我需要一个容器及其所装物品的总重量。 (我用它来预测装满露营设备的大背包的重量,而无需打包和称重。)这通过递归很容易做到,而通过循环则困难得多。但许多自然适合一种方法的问题也可以用另一种方法来解决。
You don't really need recursion for a flat structure like that. The first code I ever used recursion in involved managing physical containers. Each container might contain stuff (a list of them, each with weights) and/or more containers, which have a weight. I needed the total weight of a container and all it held. (I was using it to predict the weight of large backpacks full of camping equipment without packing and weighing them.) This was easy to do with recursion and would have been a lot harder with loops. But many kinds of problems that naturally suit themselves to one approach can also be tackled with the other.
堆栈溢出。
不,我的意思不是网站或其他东西。我的意思是“堆栈溢出”。
Stack overflow.
And no, I don't mean a website or something. I MEAN a "stack overflow".