对于我自己的学习和实践,我尝试在Javascript中实现一个函数,该函数将使用从1到参数'limit'的整数填充数组。 我做的一种方法是使用for循环:
function getRange(limit) {
const range = [];
for (let i = 1; i <= limit; i++) {
range.push(i);
}
return range;
}
然后,我想再次尝试使用递归函数编写它,并想出以下内容:
function recGetRange(limit, array) {
const range = array || [];
if (limit > 0) {
range.push(limit);
recGetRange(limit - 1, range);
}
return range.reverse();
}
现在两者似乎都运行良好,但是当试用大数字时,两者似乎也都失败了。然而,递归选项很早就失败了。我不完全确定,但for循环似乎至少适用于1E4或1E5倍的数字。 我在这些实现中做错了什么,或者即使尝试这样的事情,它只是一个死胡同? 谢谢!
答案 0 :(得分:1)
递归是命令式循环与附加堆栈结构的功能等价。
在getRange
中,您不能使用额外的堆栈结构,只能使用普通的for
循环。这就是为什么你可以用尾递归来表达getRange
的原因,其中递归调用是递归函数体内的最后一个表达式。尾递归为整个递归迭代共享一个调用堆栈帧,即不再可能发生堆栈溢出。
不幸的是,Javascript引擎还不支持尾递归优化(TCO)。因此堆栈溢出。但他们最终会支持TCO。
这是一种更通用的方法,遵循功能范例。 sequence
是一个高阶函数,它采用步进函数递归地创建序列。结果累积在一个数组(acc
)中。现在,您可以生成具有排序的每种数据类型的序列:
const sequence = stepper => (x, y) => {
const aux = (acc, z) => z <= y // inner auxiliary function
? aux(acc.concat(z), stepper(z)) // recursive case
: acc; // base case
return aux([], x);
};
const inc = x => x + 1;
const dbl = x => x * 2;
const succ = x => String.fromCharCode(x.charCodeAt(0) + 1);
console.log(sequence(inc) (1, 5)); // [1, 2, 3, 4, 5]
console.log(sequence(dbl) (2, 32)); // [2, 4, 8, 16, 32]
console.log(sequence(succ) ("c", "g")); // ["c", "d", "e", "f", "g"]
&#13;
生成器函数是另一种创建序列的方法。请注意,您不再需要累加器,因为生成器功能是有状态的。要存储序列,您必须将函数应用于支持Iterable
协议的复合类型(如Array.from
):
const sequence = stepper => (x, y) => {
function* aux() {
while (true) {
yield x;
x = stepper(x);
if (x > y) break;
}
}
return aux();
};
const sqr = x => x * x;
console.log(Array.from(sequence(sqr) (2, 256))); // [2, 4, 16, 256]
&#13;
答案 1 :(得分:0)
递归的更“规范”形式(没有传递数组和反转)将是:
function range(limit) {
// end condition
if (limit <= 0) return [];
// main recursion
var l = range(limit-1);
l.push(limit);
return l;
}
这与您的几乎相同,但它具有更“常用”的结构。