Javascript中的一个范围函数递归写入

时间:2017-03-13 18:51:50

标签: javascript recursion stack-overflow tail-recursion

对于我自己的学习和实践,我尝试在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倍的数字。 我在这些实现中做错了什么,或者即使尝试这样的事情,它只是一个死胡同? 谢谢!

2 个答案:

答案 0 :(得分:1)

尾递归和TCO

递归是命令式循环与附加堆栈结构的功能等价。

getRange中,您不能使用额外的堆栈结构,只能使用普通的for循环。这就是为什么你可以用尾递归来表达getRange的原因,其中递归调用是递归函数体内的最后一个表达式。尾递归为整个递归迭代共享一个调用堆栈帧,即不再可能发生堆栈溢出。

不幸的是,Javascript引擎还不支持尾递归优化(TCO)。因此堆栈溢出。但他们最终会支持TCO。

这是一种更通用的方法,遵循功能范例。 sequence是一个高阶函数,它采用步进函数递归地创建序列。结果累积在一个数组(acc)中。现在,您可以生成具有排序的每种数据类型的序列:

&#13;
&#13;
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;
&#13;
&#13;

生成器功能

生成器函数是另一种创建序列的方法。请注意,您不再需要累加器,因为生成器功能是有状态的。要存储序列,您必须将函数应用于支持Iterable协议的复合类型(如Array.from):

&#13;
&#13;
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;
&#13;
&#13;

答案 1 :(得分:0)

递归的更“规范”形式(没有传递数组和反转)将是:

function range(limit) {
  // end condition
  if (limit <= 0) return [];
  // main recursion
  var l = range(limit-1);
  l.push(limit);
  return l;
}

这与您的几乎相同,但它具有更“常用”的结构。