第一类迭代器

Nim中有两种迭代器: inlineclosure 迭代器。 一个 内联迭代器 是一个迭代器,总是由编译器内联,导致抽象的开销为零,但可能导致代码大小的大量增加。

注意:内联迭代器上的for循环体被内联到迭代器代码中出现的每个 yield 语句中,因此理想情况下,代码应该被重构为包含单个yield,以避免代码膨胀。

内联迭代器是二等公民; 它们只能作为参数传递给其他内联代码工具,如模板、宏和其他内联迭代器。

与此相反, 闭包迭代器 可以更自由地传递:

  1. iterator count0(): int {.closure.} =
  2. yield 0
  3.  
  4. iterator count2(): int {.closure.} =
  5. var x = 1
  6. yield x
  7. inc x
  8. yield x
  9.  
  10. proc invoke(iter: iterator(): int {.closure.}) =
  11. for x in iter(): echo x
  12.  
  13. invoke(count0)
  14. invoke(count2)

闭包迭代器和内联迭代器有一些限制:

  • 目前,闭包迭代器无法在编译时执行。
  • 在闭包迭代器中允许 return 但在内联迭代器中不允许(但很少有用)并结束迭代。
  • 内联和闭包迭代器都不能递归。
  • 内联和闭包迭代器都没有特殊的 result 变量。
  • js后端不支持闭包迭代器。迭代器既没有标记为 {.closure.} 也不是 {.inline.} 则显式默认内联,但这可能会在未来版本的实现中发生变化。

iterator 类型总是隐式调用约定 closure ;以下示例显示如何使用迭代器实现 协作任务 系统:

  1. # 简单任务:
  2. type
  3. Task = iterator (ticker: int)
  4.  
  5. iterator a1(ticker: int) {.closure.} =
  6. echo "a1: A"
  7. yield
  8. echo "a1: B"
  9. yield
  10. echo "a1: C"
  11. yield
  12. echo "a1: D"
  13.  
  14. iterator a2(ticker: int) {.closure.} =
  15. echo "a2: A"
  16. yield
  17. echo "a2: B"
  18. yield
  19. echo "a2: C"
  20.  
  21. proc runTasks(t: varargs[Task]) =
  22. var ticker = 0
  23. while true:
  24. let x = t[ticker mod t.len]
  25. if finished(x): break
  26. x(ticker)
  27. inc ticker
  28.  
  29. runTasks(a1, a2)

内置的 system.finished 可用于确定迭代器是否已完成其操作;尝试调用已完成其工作的迭代器时不会引发异常。

注意使用 system.finished 容易出错,因为它只在迭代器完成下一次迭代返回 true

  1. iterator mycount(a, b: int): int {.closure.} =
  2. var x = a
  3. while x <= b:
  4. yield x
  5. inc x
  6.  
  7. var c = mycount # 实例化迭代器
  8. while not finished(c):
  9. echo c(1, 3)
  10.  
  11. # 生成
  12. 1
  13. 2
  14. 3
  15. 0

而是必须使用此代码:

  1. var c = mycount # 实例化迭代器
  2. while true:
  3. let value = c(1, 3)
  4. if finished(c): break # 并且丢弃 'value'!
  5. echo value

它用于迭代器返回一对 (value,done)finished 用于访问隐藏的 done 字段。

闭包迭代器是 可恢复函数 ,因此必须为每个调用提供参数。 为了解决这个限制,可以捕获外部工厂proc的参数:

  1. proc mycount(a, b: int): iterator (): int =
  2. result = iterator (): int =
  3. var x = a
  4. while x <= b:
  5. yield x
  6. inc x
  7.  
  8. let foo = mycount(1, 4)
  9.  
  10. for f in foo():
  11. echo f