简体   繁体   中英

Iterating with while loop instead of for loop

ECMAScript 6 introduces generators, iterators and syntax sugar for iteration. Node.JS v0.11.4 with the flags

--harmony --use_strict --harmony_generators

understands the following generator

function* fibonacci() {
  let previous = 0;
  let current = 1;

  while(true) {
    let temp = previous;
    previous = current;
    yield current = temp + current;
  }
}

I can then print the Fibonacci numbers less than 1000.

for(let value of fibonacci()) {
    if(value > 1000) { break; }
    console.log(value);
}

For this example a while loop instead of a for loop would be more natural, similar to

while(value of fibonacci() < 1000) {
    console.log(value);
}

Can iteration of iterators be done with a while loop instead of a for loop?

Would something like this satisfy you?

var sequence = fibonacci();
var value;
while ((value = sequence.next()) < 1000) {
    console.log(value);
}

plus, maybe even a nicer solution would be something like:

function* fibonacci(limit){
  let previous = 0;
  let current = 1;

  while(previous + current < limit) {
    let temp = previous;
    previous = current;
    yield current = temp + current;
  }
}

for(let value of fibonacci(1000)) {
    console.log(value);
}

There are two possible ways I'd go on about this, given other languages that support this behavior:

1) One using Harmony proxies, which would let you doing meta-tables (kind of like in lua ) and allow for lazy iterables. This would provide fhe following notation:

var arr = ...; // create the resource
for(var i=0;arr[i]<1000;i++){
    arr[i]; // consume fibonacci numbers
}

2) The second using a take function letting you consume an iterable with .forEach like in C# or python . Which would allow the following notation:

 takeWhile(fibGenerator,(item) => item<1000).forEach(... // consume with predicate

First approach - using harmony proxies

Note... for of loops through objects. It does not guarantee order at all. You can however do something like the following to get the notion of a lazy iterate.

You have to run node both with the --harmony_generators and --harmony_proxies flags:

var arr = ...; // create an array and proxy it, use a generator internally
arr[50]; // will calculate the 50th fibonacci element and return it.
arr[100];// will calculate the 100th fibonacci element and return it.
for(var i=0;arr[i]<1000;i++){
   arr[i];//the i-th fibonacci number
}

It will only calculate numbers not fetched yet, this will allow you to use a simple for loop.

Here is how*:

var cache = [];
var handler = {
        get: (function(){
          function fibIterator(){
             var t=0,a=0,b=0;
             return function(){
                t=a;
                a+=b;
                b=t;
                return a;
             }
          }
          var iterator = fibIterator();
          return function (target, fibNumber) {
                if (name in cache) {
                    return cache[name];
                }
                while(iterator < fibNumber){
                    // update indexes. 
                }
           })()
        }
    };
var arr = Proxy.create(handler);

(Just don't expect it to be very fast)

*(using old proxy notation, since the new one isn't supported in node yet, will update once it gets support)


Side note, in JavaScript since functions can have internal state through closures, you don't even really need a generator

Second approach, using an iterator Take function.

This is what you'd normally do in languages like C# for this use case.

function takeWhile(generating, predicate){
    var res = [],last;
    do{
        res.push(last=generating())
    }while(predicate(last));
    return res;
} 

Then do something like

var res = takeWhile(fibIterator,function(item){
    return item<1000;
});
res.forEach(function(){ ...

Or by count:

function take(generating,numToTake){
    var res = [],num;
    do{
        res.push(last=generating())
    }while(num++ < numToTake);
    return res;
}

var res = take(fibIterator,1000);//first 1000 numbers

 function *bar(){ yield 1; yield 2; yield 3; return 4; } var value, g = bar(); while((value = g.next()).value){ console.log(value); } 

//Object {value: 1, done: false}
//Object {value: 2, done: false}
//Object {value: 3, done: false}
//Object {value: 4, done: true}

Yes, it is possible to do this by using the regular generator methods.

var fib = fibonacci(), value;
while( (value = fib.next()) < 1000 ) {
    console.log(value);
}

Although I seem to prefer the for...of statement which takes care of handling those next calls and dealing with StopIteration (if the sequence is finite).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM