4

I need to make a function generator that iterates over an infinite sequence, like the fibonacci sequence. It should return the next value in the sequence when called. I am given a function prototype:

function genfib() {
  return function fib() {
  }
}

it should be used like this:

var fib = genfib();
fib(); // -> returns 0
fib(); // -> returns 1
fib(); // -> returns 1
fib(); // -> returns 2

I am confused about what is executing every time I call fib(). I tried to do something like

function genfib() {
  var count = 1;
  if (count === 1) {
    count++;
    yield 0;
  }
  else if (count === 2) {
    count++;
    yield 1;
  }
  var a = 0;
  var b = 1;
  return function fib() {
    while(1) {
      count = a + b;
      a = b;
      b = count;
      yield count;
    }
  }
}

But it's not working. I don't know how to set it up to run the if/else for the first two numbers in the fib sequence and then run the while loop once for each subsequent call.

Weafs.py
  • 22,731
  • 9
  • 56
  • 78
user137717
  • 2,005
  • 4
  • 25
  • 48
  • 2
    Do you know what `yield` does? – ncksllvn Oct 30 '14 at 04:40
  • i think it's kind of like a return that only pauses a function instead of terminating it after returning a value – user137717 Oct 30 '14 at 04:42
  • 2
    If this is an assignment/homework (from "*I am given a function prototype*"), then it looks like you are supposed to learn about closures. Not write generators with `yield`. – Bergi Oct 30 '14 at 05:14
  • If our answers have been helpful to you, please mark one one of the answers as correct. – ncksllvn Oct 30 '14 at 15:22
  • @Bergi this is not homework. i'm doing exercises on codewars to get better with javascript. there is no information about a desired approach other than the given method stub and the term "function generator" in the description – user137717 Oct 30 '14 at 16:45
  • 1
    @user137717: Much the same :) But yes, that method stub is a definitive hint at using a closure. – Bergi Oct 30 '14 at 18:28

4 Answers4

7

If you want to use ES6 generators and yield, then here's the approach:

function *fibonacci() {
    var [prev, current] = [0, 1];

    while (true) {
        [prev, current] = [current, current+prev];
        yield current;
    }
}

One way to iterate over the results is with a for-of loop:

for (var v of fibonacci()) {
    console.log(v);
    if (v > 100) break;
}

Note that the destructuring assignment var [prev, current] = is supported in FF and Traceur but not in Chrome or node at present. If necessary rewrite it as:

function *fibonacci() {
    var prev = 0, current = 1, oldprev;

    while (true) {
        oldprev = prev;
        prev = current;
        yield current += oldprev;
    }
}

If you want the semantics of the function prototype you were given, then:

function genfib() {
    var iterator = fibonacci();
    return function fib() {
        return iterator.next().value;
    };
}
  • "var [prev, current]" - does that actually work? Since when? I don't think it's in ES5, and Chrome doesn't support ES6 yet (without a flag) – John Dvorak Oct 30 '14 at 05:00
  • This is a really cool answer - just don't feel like switching browsers or firing up a server to run it :) – ncksllvn Oct 30 '14 at 05:10
  • 1
    If this is a homework assignment, the OP will look like such a ninja turning this in. – ncksllvn Oct 30 '14 at 05:22
2

If you ask me, yield has no place in this function, just some clever use of JavaScript closure.

You had the right idea in the beginning - you do need a function that returns a function. Outside of the inner function have a couple variables - one for the old, one for the next. Inside the function, all you have to do is calculate the new next value, and then set old to the next's previous value. To switch their values, you'll need a placeholder variable.

function genfib() {
  var next = 1
  var old = 0
  return function fib() {
    var newNext= next + old
    old = next
    next = newNext
    return next
  }
}

var fib = genfib()

var result = []

for ( var i = 0; i < 10; i++ )
  result.push( fib() )

document.body.innerHTML = result.join()

Of course, this doesn't account for the the first function call, which is a special case (1 should be returned twice.) But I'll leave that to you to figure out :-)

Community
  • 1
  • 1
ncksllvn
  • 5,699
  • 2
  • 21
  • 28
  • how is the state of fib preserved between calls? Why doesn't all that information get deleted when the function returns? – user137717 Oct 30 '14 at 05:10
  • 1
    JS is a funky language if you come from a Java background. It'll take some time, but you have to grasp JS closure that I linked to. – ncksllvn Oct 30 '14 at 05:11
1
function* fib(num) {
  var a = num, b = a + 1, c = a;

  while (true) {
    yield a;
    c = a;
    a = b;
    b = c + b;
  }
}

var it = fib(0);
console.log(it.next().value); // 0
console.log(it.next().value); // 1
console.log(it.next().value); // 1
console.log(it.next().value); // 2
console.log(it.next().value); // 3
console.log(it.next().value); // 5
console.log(it.next().value); // 8
console.log(it.next().value); // 13

For a high level overview on how to use generators, checkout this post.

Denim Demon
  • 734
  • 1
  • 10
  • 23
0
function* fibonacci(){
  var fn1 = 1;
  var fn2 = 1;
  while (true){  
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    var reset = yield current;
    if (reset){
        fn1 = 1;
        fn2 = 1;
    }
  }
}

var sequence = fibonacci();
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
console.log(sequence.next().value);     // 3
console.log(sequence.next().value);     // 5
console.log(sequence.next().value);     // 8
console.log(sequence.next().value);     // 13
console.log(sequence.next(true).value); // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
console.log(sequence.next().value);     // 3