Dart中yield和yield*之间的差异

mir*_*cal 34 dart flutter

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

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

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

Cop*_*oad 47

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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

1
2
3
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

3
2 
1 
Run Code Online (Sandbox Code Playgroud)


Sur*_*gch 29

简答

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

例子

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

表格 1

这是一个从startto 开始计数的 Iterable finish

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

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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这有效,但由于循环而难以阅读且效率不高。

表格 3

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

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

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


aba*_*nny 5

我创建了一个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);
  }
}
Run Code Online (Sandbox Code Playgroud)

在第一个实现中 (yield i) 意味着 Iterable 类型与函数的返回类型相匹配。

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

 yield* getRangeRecursive(start + 1, finish);
Run Code Online (Sandbox Code Playgroud)

如果我们这样做了

yield getRangeRecursive(start + 1, finish);
Run Code Online (Sandbox Code Playgroud)

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

The type 'Iterable<Iterable<int>>' implied by the 'yield' expression must be assignable to 'Iterable<int>'.
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,yield 用另一个 Iterable<> 包装了 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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这是混乱且低效的。

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

一些很好的资源: 生成器函数 - Flutter in Focus 视频

中型文章: Dart 中的sync*、async*、yield 和yield* 是什么?