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
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
Share Improve this question edited May 23, 2017 at 10:29 CommunityBot 11 silver badge asked May 12, 2016 at 6:23 Aditya SinghAditya Singh 16.7k15 gold badges48 silver badges69 bronze badges 13- Ok. I think that's wrong. Let me update it – Aditya Singh Commented May 12, 2016 at 6:31
- You are not returning anything.. and you are saying that this is a recursive call. – Rajaprabhu Aravindasamy Commented May 12, 2016 at 6:31
-
Yeah. With
yield
you don't need to return. Refer: 2ality./2013/06/iterators-generators.html – Aditya Singh Commented May 12, 2016 at 6:32 - Why does he need to return anything? – user663031 Commented May 12, 2016 at 6:33
-
1
fact
returns an iterator. What do you expect the result of multiplying a number with an iterator (n * fact(n-1)
) to be? – Felix Kling Commented May 12, 2016 at 6:33
2 Answers
Reset to default 9Can 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 ]