关于JavaScript,我可以看到两种不同的编写自引用函数的方法:
Divide和Conquer-like算法,例如阶乘或合并排序,其中值是从不断减小的样本中递归计算的:
const factorialOf20 = (function factorial(n) {
return (n == 0) ? 1 : n * factorial(n - 1);
})(20);
通过自调用函数循环 (当循环包含异步执行的代码时 - 例如网络请求 - 我发现这很有用但是循环迭代需要被连载)。
var result = '';
(function doLoop(i) {
if (i < 10) {
i++;
request('http://...', function(err, res, body) {
result += body;
doLoop(i);
});
};
})(0);
在JavaScript中,当通过递归进行循环时,每个函数调用都会设置一个新的执行上下文,并且对于调用不同函数的情况看起来(对我来说)并没有任何不同。
这仍然被认为是递归吗?
答案 0 :(得分:3)
我想在你的第二个例子中你实际上意味着像
function doLoop() {
if (hasAsyncWork)
doAsyncWork({callback: doLoop})
}
所以函数调度自己在稍后的某个时刻运行,而不是直接调用自己。这种模式在javascript中使用很多并且有很多变化,例如:
function fun() {
do something
setTimeout(fun, 100)
}
或
function fun() {
return doAsyncWork().then(fun)
}
甚至
async function fun(jobs) {
if (jobs.length)
await fun(jobs.slice(1))
}
从技术上讲,这不是递归,至少在传统意义上是这样。
答案 1 :(得分:3)
这仍然被认为是递归吗?
最终,这更多的是术语和意见,而不是纯粹的技术问题。但是,请考虑以下内容:
function A(num)
{
if (num < 3) B(num);
}
function B(num)
{
A(num - 1);
}
也一直被认为是递归; A
和B
都没有直接调用自己,但整个调用链中都有递归。因此,A
和B
都是递归的,因为A
仍会导致调用A
,同样B
。
因此,您的示例类似于doLoop
未调用doLoop
,但它确实会导致doLoop
被调用。
如果某些人反对调用此递归,那么doLoop
将不会成为之前doLoop
的调用链的一部分,或者可能不会(#&} 39;可以编写版本,但不清楚是否立即调用doLoop
。这个更具体的递归定义在考虑如何实现语言时(例如,是否存在调用堆栈或其他允许递归的机制)比使用它们的方式更有用。
答案 2 :(得分:2)
也许与georg相反,我认为georg和你的异步示例实际上是传统意义上的递归示例。让我们看一下维基百科中关于递归关系的定义:
...an equation that recursively defines a sequence or
multidimensional array of values, once one or more
initial terms are given: each further term of the sequence
or array is defined as a function of the preceding terms.
重要的是,异步调用是否定义了一系列值(想想&#34;值&#34;抽象地),每个都来自前一个术语。这一代人是否被延迟甚至是有条件的似乎与我无关。