【问题标题】:Iterating with while loop instead of for loop使用 while 循环而不是 for 循环进行迭代
【发布时间】:2013-07-07 13:49:26
【问题描述】:

ECMAScript 6 为迭代引入了生成器、迭代器和语法糖。带有标志的 Node.JS v0.11.4

--harmony --use_strict --harmony_generators

理解下面的生成器

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 循环来完成迭代器的迭代吗?

【问题讨论】:

    标签: javascript ecmascript-6


    【解决方案1】:

    您可以使用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);
    }
    

    【讨论】:

    • @Randomblue 我认为这是 OP 问题中的错误,而不是答案。
    • @fsw 这是notation for generators
    • @AaditMShah 不,根据规范function* 将函数标记为生成器。
    • @fsw Mozilla 当前的生成器实现是个笑话 :) 这就是原因。不过老实说,他们只是实现了生成器(以及for... of,顺便说一下,不在规范中)之前在规范中,这就是为什么他们做一些愚蠢的事情,比如在迭代完成时抛出异常(比如在 python 生成器中,与 C# 生成器不同)。
    • 更正我的最后一条评论,因为它不能再修改了 - 除了 .forEach 之外,显然 for... of 循环在规范中是 back 的。很抱歉提供错误信息。
    【解决方案2】:

    考虑到支持这种行为的其他语言,我有两种可能的方法:

    1) 一个使用 Harmony 代理,它可以让你做元表(有点像 lua 中的)并允许惰性迭代。这将提供以下符号:

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

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

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

    第一种方法 - 使用和谐代理

    注意...for of 循环遍历对象。它根本不保证顺序。但是,您可以执行以下操作来获得惰性迭代的概念。

    您必须使用 --harmony_generators--harmony_proxies 标志运行节点:

    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 如果您认为这很有帮助 - 当然可以。你更喜欢哪个版本? (所以它可以保持投票计数)
    【解决方案3】:

    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}
    

    【讨论】:

    • 回答一个已经接受答案的 2 年前的问题,您可能想要解释什么解决方案带来了以前没有的解决方案。也许在一小段中?
    • 可以在stackoverflow中练习
    • 这个工作。它以未完成的价值观结束。在yield 2 之后插入yield 0 并观察它失败。你可能想要while(!(value = g.next()).done){
    【解决方案4】:

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

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

    虽然我似乎更喜欢 for...of 语句,它负责处理接下来的调用并处理 StopIteration(如果序列是有限的)。

    【讨论】:

      猜你喜欢
      • 2010-09-11
      • 2011-10-06
      • 2021-02-24
      • 2020-08-14
      • 1970-01-01
      • 2020-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多