使用while循环迭代而不是for循环

42
ECMAScript 6 引入生成器(generator)、迭代器(iterators)和语法糖(syntax sugar)来进行迭代。 Node.JS v0.11.4 可以通过以下标识来使用:

--harmony --use_strict --harmony_generators

可以理解下面的生成器(generator):

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

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

我可以打印小于1000的斐波那契数列。

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

对于这个示例,使用 while 循环而不是 for 循环会更加自然,类似于:

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

使用 while 循环而不是 for 循环,能否迭代迭代器?

4个回答

15
你可以使用next函数逐步调用生成器。
var sequence = fibonacci();
var value;
while ((value = sequence.next()) < 1000) {
    console.log(value);
}

另外,也许更好的解决方案是:

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);
}

1
@Randomblue,我认为这是提问者问题的错误,而不是回答的错误。 - Aadit M Shah
3
@fsw 这是生成器的符号表示法 - Benjamin Gruenbaum
4
不,根据规范,function* 表示函数是一个生成器。 - a better oliver
2
@fsw Mozilla目前的生成器实现很糟糕 :) 这就是为什么。但说实话,他们只是在规范之前实现了生成器(以及for...of,顺便说一下,这也不在规范中),这就是为什么它们会做一些傻事,比如在迭代完成后抛出异常(与Python生成器不同,类似于C#生成器)。 - Benjamin Gruenbaum
1
更正我的上一条评论,因为它无法再被修改 - 显然,for...of循环在规范中又回来了,除了.forEach之外。对于错误信息,我感到抱歉。 - Benjamin Gruenbaum
显示剩余6条评论

7

考虑到其他支持此行为的语言,我会采取两种可能的途径:

1) 使用Harmony代理,类似于Lua中的元表,可以允许使用惰性迭代器,提供以下注释:

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

2) 第二种方法使用 take 函数,让您可以像在C#python中一样使用 .forEach 消耗可迭代对象。这将允许以下符号:

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

第一种方法 - 使用 harmony proxies

注意...for of 循环遍历对象。它根本不保证顺序。您可以尝试以下代码实现惰性迭代。

你必须使用 --harmony_generators--harmony_proxies 标志运行node:

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
}

它只会计算还未获取的数字,这样您就可以使用简单的for循环。

这是方法*:

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);

(只是不要期望速度会很快)

*(使用旧的代理符号,因为新的符号在节点上还没有得到支持,等它得到支持后将进行更新)


另外,由于 JavaScript 中函数可以通过闭包具有内部状态,因此您实际上甚至不需要生成器

第二种方法,使用迭代器 Take 函数。

这是在像 C# 这样的语言中通常用于此用例的方法。

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

然后做类似以下的操作
var res = takeWhile(fibIterator,function(item){
    return item<1000;
});
res.forEach(function(){ ...

或者按计数:

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

@Randomblue 如果你认为这很有帮助 - 当然可以。你更喜欢哪个版本?(这样它就可以保留投票数) - Benjamin Gruenbaum

1

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}

回答一个已经有接受答案的两年前的问题时,您可能需要解释这个解决方案与以前不同的地方。也许可以写一段简短的段落来说明? - Benjamin W.
也许可以在Stack Overflow上练习。 - bofredo
1
这个不起作用。它以未完成的值结束。在yield 2后插入yield 0并观察其失败。你可能想要while(!(value = g.next()).done){ - gman

0

是的,可以通过使用常规生成器方法来实现这一点。

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

虽然我似乎更喜欢使用 for...of 语句,它可以处理那些下一个调用并处理 StopIteration(如果序列是有限的)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接