• 第一类迭代器

    第一类迭代器

    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