【问题标题】:Difference between yield and yield* in DartDart 中的 yield 和 yield* 之间的差异
【发布时间】:2019-08-14 10:02:57
【问题描述】:

我想知道这两者之间有什么区别。我在 javascript 上找到了这个 SO 帖子,Delegated yield (yield star, yield *) in generator functions

据我了解,yield* 委托给另一个生成器,在另一个生成器停止生成值后,它会继续生成自己的值。

飞镖方面的解释和示例会很有帮助。

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    yield

    它用于从生成器发出异步或同步的值。

    示例:

    Stream<int> str(int n) async* {
      for (var i = 1; i <= n; i++) {
        await Future.delayed(Duration(seconds: 1));
        yield i;
      }
    }
    
    void main() {
      str(3).forEach(print);
    }
    

    输出:

    1
    2
    3
    

    yield*

    它将调用委托给另一个生成器,在该生成器停止生成值后,它会继续生成自己的值。

    示例:

    Stream<int> str(int n) async* {
      if (n > 0) {  
        await Future.delayed(Duration(seconds: 1));
        yield n;
        yield* str(n - 1);
      }
    }
    
    void main() {
      str(3).forEach(print);
    }
    

    输出:

    3
    2 
    1 
    

    【讨论】:

    • 如果我在生成器中使用yield numbersDownFrom(n - 1) 而不是yield* numbersDownFrom(n - 1); 会发生什么?
    • @sonic yield* 用于将调用委托给生成器,yield numbersDownFrom(n-1) 没有任何意义,因为yield 只会产生您想要在Stream 中返回的类型。
    • 我需要自己测试一下。我只是好奇它是否会被标记为错误或者它会做一些意想不到的事情。感谢您的回答。
    • 答案开头有一个错误:句子'It is used to emit values from a generator or async or sync.'应该是“它用于从生成器发出值异步*或同步*。”见jelenaaa.medium.com/…
    【解决方案2】:

    简答

    • yield 从 Iterable 或 Stream 返回值。
    • yield* 用于递归调用其 Iterable 或 Stream 函数。

    示例

    让我们看一些来自Generator Functions - Flutter in Focus 视频的示例。我们将只看 Iterable 示例,但 Streams 类似。

    表格 1

    这是一个从 start 计数到 finish 的 Iterable。

    Iterable<int> getRange(int start, int finish) sync* {
      for (int i = start; i <= finish; i++) {
        yield i;
      }
    }
    

    yield 关键字在每次迭代中返回下一个值。

    表格 2

    现在让我们将该函数重构为递归。在外面它仍然和以前一样:

    Iterable<int> getRange(int start, int finish) sync* {
      if (start <= finish) {
        yield start;
        for (final val in getRange(start + 1, finish)) {
          yield val;
        }
      }
    }
    

    这可行,但由于循环,它很难阅读并且效率不高。

    表格 3

    现在让我们使用yield*(读作“yield star”)再次重构它:

    Iterable<int> getRange(int start, int finish) sync* {
      if (start <= finish) {
        yield start;
        yield* getRange(start + 1, finish);
      }
    }
    

    它仍然是递归的,但现在它更易于阅读且效率更高。

    【讨论】:

      【解决方案3】:

      我创建了一个dart pad 链接来帮助人们进行实验:

      Yield* 用于在不使用循环的情况下一次产生整个可迭代的一个值。

      这两个函数做同样的事情,根据开始和结束值生成一个迭代。

      Iterable<int> getRangeIteration(int start, int finish) sync* {
        for(int i = start; i<= finish; i++){
        yield i;
        }
      }
      
      Iterable<int> getRangeRecursive(int start, int finish) sync* {
        if (start <= finish) {
          yield start;
          yield* getRangeRecursive(start + 1, finish);
        }
      }
      

      在第一个实现中(yield i)将暗示与函数的返回类型匹配的 Iterable 类型。

      现在如果在第二个实现中而不是

       yield* getRangeRecursive(start + 1, finish);
      

      如果我们这样做了

      yield getRangeRecursive(start + 1, finish);
      

      我们会得到一个编译器错误:

      The type 'Iterable<Iterable<int>>' implied by the 'yield' expression must be assignable to 'Iterable<int>'.
      

      正如你所见,yield 用另一个 Iterable 包装了 Iterable,这使得类型为 Iterable。与函数的返回类型不匹配。

      如果我们必须在不使用 yield* 的情况下进行递归,我们将不得不这样做:

      Iterable<int> getRangeRecursiveWithOutYieldStar(int start, int finish) sync* {
        if (start <= finish) {
          yield start;
          for (final val in getRangeRecursiveWithOutYieldStar(start + 1, finish)){
          yield val;
          }
        }
      }
      

      这是混乱和低效的。

      所以我觉得在我看来,yield* 会扁平化另一个生成器函数。

      一些好的资源: Generator Functions - Flutter in Focus video

      中篇文章: What are sync*, async*, yield and yield* in Dart?

      【讨论】:

        【解决方案4】:

        使用下面的代码来理解为什么不使用 yield* 进行递归编码真的是低效的。

        Iterable<int> getRangeYield(int start, int end) sync* {
          if (start <= end) {
            yield start;
            for (final int val in getRangeYield(start + 1, end)) {
              yield val;
            }
          }
        }
        
        Iterable<int> getRangeYieldAnalysed(int start, int end) sync* {
          if (start <= end) {
            print('before start $start');
            yield start * 10;
            print('after start $start');
            for (final int val in getRangeYieldAnalysed(start + 1, end)) {
              print('before val $val');
              yield val * 100;
              print('after val $val');
            }
          }
        }
        
        Iterable<int> getRangeYieldStar(int start, int end) sync* {
          // same output as getRangeYield()
          if (start < end) {
            yield* getRangeYieldStar(start + 1, end);
          }
          yield start;
        }
        
        Iterable<int> getRangeYieldStarAnalysed(int start, int end) sync* {
          // same output as getRangeYield()
          print('generator $start started');
          if (start < end) {
            yield* getRangeYieldStarAnalysed(start + 1, end);
          }
          yield start;
          print('generator $start ended');
        }
        
        Iterable<int> getRangeForLoop(int start, int end) sync* {
          // same output as getRangeYield()
          for (int i = start; i <= end; i++) {
            yield i;
          }
        }
        
        void main() {
          Iterable<int> it = getRangeYieldStarAnalysed(1, 4);
          print('main range obtained');
        
          for(int element in it) {
            print('el $element');
          };
        }
        

        【讨论】:

          猜你喜欢
          • 2021-12-27
          • 2010-12-14
          • 2018-09-25
          • 1970-01-01
          • 1970-01-01
          • 2014-11-11
          • 2015-05-18
          • 2013-04-06
          • 2019-09-20
          相关资源
          最近更新 更多