- 第一类迭代器
第一类迭代器
Nim中有两种迭代器: inline 和 closure 迭代器。 一个 内联迭代器 是一个迭代器,总是由编译器内联,导致抽象的开销为零,但可能导致代码大小的大量增加。
注意:内联迭代器上的for循环体被内联到迭代器代码中出现的每个 yield 语句中,因此理想情况下,代码应该被重构为包含单个yield,以避免代码膨胀。
内联迭代器是二等公民; 它们只能作为参数传递给其他内联代码工具,如模板、宏和其他内联迭代器。
与此相反, 闭包迭代器 可以更自由地传递:
- iterator count0(): int {.closure.} =
- yield 0
- iterator count2(): int {.closure.} =
- var x = 1
- yield x
- inc x
- yield x
- proc invoke(iter: iterator(): int {.closure.}) =
- for x in iter(): echo x
- invoke(count0)
- invoke(count2)
闭包迭代器和内联迭代器有一些限制:
- 目前,闭包迭代器无法在编译时执行。
- 在闭包迭代器中允许 return 但在内联迭代器中不允许(但很少有用)并结束迭代。
- 内联和闭包迭代器都不能递归。
- 内联和闭包迭代器都没有特殊的 result 变量。
- js后端不支持闭包迭代器。迭代器既没有标记为 {.closure.} 也不是 {.inline.} 则显式默认内联,但这可能会在未来版本的实现中发生变化。
iterator 类型总是隐式调用约定 closure ;以下示例显示如何使用迭代器实现 协作任务 系统:
- # 简单任务:
- type
- Task = iterator (ticker: int)
- iterator a1(ticker: int) {.closure.} =
- echo "a1: A"
- yield
- echo "a1: B"
- yield
- echo "a1: C"
- yield
- echo "a1: D"
- iterator a2(ticker: int) {.closure.} =
- echo "a2: A"
- yield
- echo "a2: B"
- yield
- echo "a2: C"
- proc runTasks(t: varargs[Task]) =
- var ticker = 0
- while true:
- let x = t[ticker mod t.len]
- if finished(x): break
- x(ticker)
- inc ticker
- runTasks(a1, a2)
内置的 system.finished 可用于确定迭代器是否已完成其操作;尝试调用已完成其工作的迭代器时不会引发异常。
注意使用 system.finished 容易出错,因为它只在迭代器完成下一次迭代返回 true :
- iterator mycount(a, b: int): int {.closure.} =
- var x = a
- while x <= b:
- yield x
- inc x
- var c = mycount # 实例化迭代器
- while not finished(c):
- echo c(1, 3)
- # 生成
- 1
- 2
- 3
- 0
而是必须使用此代码:
- var c = mycount # 实例化迭代器
- while true:
- let value = c(1, 3)
- if finished(c): break # 并且丢弃 'value'!
- echo value
它用于迭代器返回一对 (value,done) 和 finished 用于访问隐藏的 done 字段。
闭包迭代器是 可恢复函数 ,因此必须为每个调用提供参数。 为了解决这个限制,可以捕获外部工厂proc的参数:
- proc mycount(a, b: int): iterator (): int =
- result = iterator (): int =
- var x = a
- while x <= b:
- yield x
- inc x
- let foo = mycount(1, 4)
- for f in foo():
- echo f