【问题标题】:Why recursive generator function doesn't work in ES2015?为什么递归生成器函数在 ES2015 中不起作用?
【发布时间】:2016-09-07 19:23:16
【问题描述】:

我正在尝试了解 ES2015 中的生成器,并用它创建了一个递归阶乘函数。但它不起作用。我已经提到了关于该主题的现有问题,例如this,但它没有帮助。

function* fact (n) {
   if (n < 2) {
     yield 1;
   } else {
     yield* (n * fact(n-1));
   }
}

let b = fact(5);
console.log(b.next()); 

谁能找到我在这里遗漏的任何明显问题?我在 JSFiddle 中使用它和 JavaScript-1.7 here

【问题讨论】:

  • 好的。我认为这是错误的。让我更新一下
  • 你没有返回任何东西..你说这是一个递归调用。
  • 是的。使用yield,您无需返回。参考:2ality.com/2013/06/iterators-generators.html
  • 他为什么要退货?
  • fact 返回一个迭代器。您期望将数字与迭代器 (n * fact(n-1)) 相乘的结果是什么?

标签: javascript recursion ecmascript-6 generator


【解决方案1】:

谁能找到我在这里遗漏的任何明显问题?

fact 返回一个迭代器,但您正尝试将它与 number 相乘:n * fact(n-1)。那行不通!

因为fact 返回一个迭代器,但您还想将迭代器的最后一个值与n 相乘(即它不是尾递归),您也不能简单地使用yield* 它。
您需要显式迭代内部调用的结果,重新发送值并记住最后一个值,以便您可以使用它:

function* fact (n) {
   if (n < 2) {
     yield 1;
   } else {
     let last;
     for(last of fact(n-1)) {
       yield last;
     }
     yield n * last;
   }
}

Array.from(fact(5)); // [1, 2, 6, 24, 120]

如果将函数更改为尾递归,它会更短一些(更好),但结果也会不同(因为我们以不同的顺序执行操作,至少在这个实现中):

function* fact (n, acc=1) {
   yield acc
   if (n > 1) {
     yield* fact(n-1, acc * n);
   }
}
Array.from(fact(5)); // [1, 5, 20, 60, 120]

我个人只会写一个非递归版本:

function* fact (n) {
  let result = 1;
  let i = 0;
  while (i < n) {
    yield result = result * ++i;
  }
}
Array.from(fact(5)); // [1, 2, 6, 24, 120]

【讨论】:

  • 第二个尾递归版本是我一直在寻找的完美解决方案。非常感谢:)
  • @Aditya 尾递归解决方案并未优化尾调用,因为生成器函数不在 TCO 范围内。因此,请考虑性能缺陷和可能的堆栈溢出。
【解决方案2】:

只是添加另一个返回所需结果的尾调用递归解决方案,但需要三个参数(与 Felix Kling 的第二个示例略有不同):

function *factorial(n, add=1, cnt=1) {
  yield add;
  if (cnt < n) {
    cnt++;
    yield* factorial(n, add * cnt, cnt);
  }
}

Array.from(factorial(5));
// Array [ 1, 2, 6, 24, 120 ]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    • 2021-10-28
    • 2023-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-02
    相关资源
    最近更新 更多