所以,我们都知道递归函数,对吧?但究竟是什么,使函数递归?我在另一个问题(Lightening effect with javascript)的评论部分进行了一个小小的讨论,这有点挑战了我对递归函数的看法,但它也让我非常不满意地缺乏正确的定义。
到目前为止,我对递归函数的个人定义如下:
如果函数直接或间接调用自身
,则该函数是递归的
注意:我将为以下示例提供JavaScript代码,但我确信它们非常普遍。
这种递归函数的一个简单示例可能是:
function a() {
a();
}
但也是这样:
function a() {
b();
}
function b() {
a();
}
甚至是这样:
function a() {
setTimeout(a, 1000);
}
这些函数都没有计算任何东西,但我仍然认为它们是递归的,因为它们会调用它们。
有一件事是,第三个例子不是递归的,因为它使用setTimeout
,因此堆栈被解开。它也不是递归的,因为从n
调用返回后它不会对n-1
调用返回控制权。
提出的另一点是,这些函数都不是递归的,因为它们都不以递归方式计算实际问题。意味着必须通过将其分成越来越小的实例来解决问题。这里引用的维基百科文章:
当函数是函数时,计算机编程中的递归就是例证 根据更简单,通常更小的版本来定义。该 然后通过组合解决方案来设计问题的解决方案 从问题的简单版本中获得。
所以这是递归的:
function fac(n) {
if (n <= 0) {
return 1;
}
return n * fac(n - 1);
}
但这不会是:
function fac(n) {
if (n <= 0) {
return 1;
}
console.log(n);
fac(n - 1);
}
那么递归函数的正确定义是什么?有没有或者它真的只是一个哲学问题?功能必须具有哪些功能才能被归类为递归?
答案 0 :(得分:2)
递归的定义?再次阅读此行,直至获得它。
(具有中止标准的自我调用功能,以防止无限循环)。
答案 1 :(得分:2)
递归只是根据一个更简单的情况(更简单的意思是“更接近”终止条件,不一定实际上更简单)来定义一个问题,直到最简单的情况为止是已知的(前面提到的终止条件)。因此,例如,多年生因子函数具有终止条件:
f(1) = 1
以更简单的方式定义问题:
f(n) = n * f(n - 1), for n > 1
我听过的最好的解释是:
我不会调用setTimeout
一个递归,因为a
不实际调用自身。相反,它要求“系统”在以后调用它。
在某处有终止条件也很重要。没有它,它仍然是递归但它是无限递归,与无限循环没有区别,如:
for (i = 0; i < 10; j++) {}
因此除了测试堆栈溢出时会发生什么之外的其他任何事情都不会有任何好处: - )
答案 2 :(得分:2)
递归是为调用自身的函数赋予的名称。现在该函数是否无限调用自身..它仍然是一个递归。
问题不一定分为子问题。但是,在计算机科学中;术语递归指的是一种用于解决问题的技术,通过将问题分解为子问题并且通常问题是有限的。
还有一点,Recursion是使用Stack实现的。每个函数调用都堆叠在堆栈中的另一个函数调用之上,直到最后一个调用满足基本条件,然后堆栈中的函数从上到下执行。
但是,如果没有基本条件或永远不满足基本条件。然后对函数的无限调用将被推送到堆栈,导致内存被填满,并且将抛出stackOverFlow异常并且OS通过终止程序来处理它。
关于setTimeout()
这是一个异步调用并且与无关到递归,它是一个独立调用,因为调用函数不依赖于被调用函数是否是被调用的函数或其他函数。
答案 3 :(得分:0)
来自您发布的维基百科:
当函数根据更简单的,通常更小的版本定义时,可以举例说明计算机编程中的递归。然后通过组合从较简单的问题版本获得的解决方案来设计问题的解决方案。
因此。有一个问题,有一个解决方案。有一个函数调用自己最小化主要问题。这个函数是递归的。
案例:
function a() {
a();
}
没有问题,没有什么可以最小化,没有解决方案。这对我来说不是递归的。这只是一个无限循环。
另一个例子:
function a(n) {
if(n<.5) {
return n+a(Math.random());
}else {
return n;
}
}
console.log(a(.3));
这是递归的吗? 没有。 可能存在问题,但没有找到最小化主要问题的解决方案。很简单,它会随机调用,直到某个标志为真。再次,这是一个循环。 setTimeout或setInterval也是如此(问题的解决方案将取决于系统调用)。