Почему рекурсивная функция генератора не работает в ES2015?

Я пытаюсь понять генераторы в ES2015 и создал с ним рекурсивную факториальную функцию. Но это не сработает. Я уже упомянул уже существующий вопрос, например , но это не помогает.

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 здесь

Ответ 1

Может ли кто-нибудь найти какие-либо очевидные проблемы, которые мне не хватает здесь?

fact возвращает итератор, но вы пытаетесь его несколько с номером: 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]

Ответ 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 ]