dart - 意外的迭代器函数和 Iterable.take 行为
问题描述
我正在编写一个名为批处理的简单函数,它应该将可迭代拆分为可迭代的大小大小的可迭代。然后我遇到了生成器函数和 Iterable.take 方法的奇怪行为(当期望/知道 python 生成器行为时)。
这段代码:
Iterable<T> iterate<T>(Iterable<T> iterable) sync* {
print('generator started');
for (var item in iterable)
yield item;
}
void main() {
List l = [1, 2, 3, 4, 5];
final it = iterate(l);
print(it.take(2));
print(it.take(2));
}
输出:
generator started
(1, 2)
generator started
(1, 2)
而预期的输出是:
generator started
(1, 2)
(3, 4)
- 为什么
iterate
调用两次而不是在产量之后继续下一次迭代? - 编写以下解决方法的任何内置或更优雅的方式?我的解决方法:
Iterable<List<T>> batches<T>(Iterable<T> iterable, int size) sync* {
final iter = iterable.iterator;
List group = takeN(iter, size).toList();
while (group.length > 0){
yield group;
group = takeN(iter, size).toList();
}
}
Iterable<T> takeN<T>(Iterator<T> iterator, int n) sync* {
for (var i = 0; i < n && iterator.moveNext(); i++)
yield iterator.current;
}
void main() {
List l = [1, 2, 3, 4, 5];
print(batches(l, 2));
}
解决方案
这就是可迭代对象和迭代器的工作方式。
AnIterable
是一个简单的对象,它在您开始迭代之前什么都不做。AnIterator
是保持迭代状态的那个。
当你调用一个sync*
函数时,它会立即返回一个Iterable
. 当您开始迭代该可迭代对象时,通过读取它的iterator
getter 并使用返回Iterator
的 ,sync*
函数体开始运行。每次调用moveNext
都会运行身体,直到下一次yield
。
每次你得到一个新iterator
的,函数体都是从头开始的。这就是为什么你的两个调用it.take(2)
都做同样的事情,每个都通过获取一个新的迭代器并调用moveNext
两次来工作。
至于您想要做的更简单的方法,也许是:
Iterable<List<T>> batch<T>(Iterable<T> source, int size) {
List<T> accumulator;
for (var value in source) {
(accumulator ??= []).add(value);
if (accumulator.length == size) {
yield accumulator;
accumulator = null;
}
}
if (accumulator != null) yield accumulator;
}
推荐阅读
- android - 使用 buildType 的动态名称和包名称以及 Gradle 的风味
- sockets - 汽车制造商服务器和客户端之间的时钟同步
- python - 何时删除烧瓶中的临时文件?
- python - 如何获取 iPhone 的加速度计数据?
- google-app-engine - 在 Google App Engine 中哪里可以找到我的应用程序的名称?
- node.js - 如何根据来自用户对话流的月份输入来验证日期
- node.js - Azure Web App 使用 Node 10.x 而不是 14.x
- imagemagick - 将图像水平分成两个不相等的部分?
- javascript - JavaScript:选项隐藏代码在某些浏览器中不起作用
- javascript - 当被 ngTemplate 块替换时,Angular 字段失去了对验证的关注