I am trying to understand generators in ES2015 and have created a recursive factorial function with it. But it doesn't work. I have referred already existing question like this on the topic, but it doesn't help.
function* fact (n) {
if (n < 2) {
yield 1;
} else {
yield* (n * fact(n-1));
}
}
let b = fact(5);
console.log(b.next());
Can anyone find any obvious issues I am missing here? I am using this in JSFiddle with JavaScript-1.7 here
Can anyone find any obvious issues I am missing here?
fact
returns an iterator, yet you are trying to multiple it with a number: n * fact(n-1)
. That cannot work!
Because fact
returns an iterator, but you also want to multiply the last value of the iterator with n
(i.e. it is not tail-recursive), you cannot simply yield*
it either.
You need to explicitly iterate over the result from the internal call, reemit the value and remember the last value so that you can multiple with it:
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]
If you change the function to be tail-recursive, it would be a bit shorter (and nicer), but the result would also be different (since we perform the operations in different order, at least in this implementation):
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]
Personally I would just write a non-recursive version:
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]
Just to add another tail-call recursive solution that returns the desired result, but takes three arguments (a slight variation from Felix Kling's second example):
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 ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With