11.6 生成器(DONE)

生成器(generatro)是一种潜在可能生成无限数据流的函数。生成器在调用时每次会生成一个值,抛出给调用者,然后将自己挂起,等待下一次的调用。

[Macro] iter-defun name args [doc] [declare] [interactive] body...

iter-defun 用来定义一个生成器函数。生成器函数和常规函数的函数签名没有什么区别,但是运行的方式会有些区别。常规函数在调用的时候会对函数体进行求值,但生成器函数不同。生成器函数会返回一个迭代器对象(iterator object)。该迭代器会运行 body 部分以生成一个值,在 iter-yield 或 iter-yield-from 处将该值返回给调用方,并暂停运行。之后某刻,

body 中可以包含任何Lisp代码,但是 iter-yield 和 iter-yield-from 不能在 unwind-protect 中使用。

[Macro] iter-lambda args [doc] [interactive] body...

iter-lambda 生成一个匿名生成器函数,该生成器与常规 iter-defun 定义的生成器没有什么区别。

[Macro] iter-yield value

该宏应该写在在生成器函数定义内。当该宏出现在生成器函数内部时,就指明当前生成器应当在此处暂停,并将 value 返回给 iter-next 处。 iter-yield 求值结果为下一次 iter-next 调用的 value 值。

[Macro] iter-yield-from iterator

iter-yield-from 会抛出 iterator 生成的所有值,其求值结果为常规生成器函数生成的值。(while

使用生成器函数,首先要先正常调用一下,使其生成一个迭代器对象。迭代器是生成器的一种特殊实例。接下来我们需要使用 iter-next 从这个迭代器中取值。当迭代器中没有值返回时,iter-next 会抛出 iter-end-of-sequence condition 与迭代器的最后值。

需要注意的是,生成器函数的函数体仅在 iter-next 的调用下才会求值运行。对生成器函数的正常调用只会让其生成迭代器;你必须使用 iter-next 取驱动这个迭代器,从中取值。每次对生成器函数的常规调用都会生成一个不同的迭代器,这些迭代器的状态都是独立的。

[Function] iter-next iterator value

从迭代器 iterator 中取出下一个值。若没有更多的值生成了(通常是因为生成器函数返回了),iter-next 会抛出 iter-end-of-sequence 条件;和这个条件一同返回的还有生成器函数返回的值。

value 会被传入迭代器 iterator,该值会成为 iter-yield 求值的结果。第一次 iter-next 调用时,value会被忽略,因为这时还在迭代器的生成器函数的顶部,还没有运行到 iter-yield 表达式。

[Function] iter-close iterator

若迭代器 iterator 在 unwind-protect 的 bodyform 内被弃置了,而且无法获取,那么 Emacs 会在垃圾回收后运行 unwind 处理器。(需要注意的是,在 unwind-protect 内的 unwindforms 中使用 iter-yield 是不合法的。)为了让处理器在垃圾回收前运行,你需要使用 iter-close。

下面还有若干辅助函数,用来更简洁的使用迭代器:

[Macro] iter-do (var iterator) body...

将 var 绑定到 iterator 生成的每个值,然后运行 body。

Common Lisp loop 包也提供了一些与迭代器一起协作的特性。这部分请查阅 Common Lisp Extensions 章的 “Loop Facility"部分。

下面的例子展示了一些使用迭代器时的重要原则。

(require 'generator)
(iter-defun my-iter (x)
  (iter-yield (1+ (iter-yield (1+ x))))
   ;; 正常返回
  -1)
  
(let* ((iter (my-iter 5))
       (iter2 (my-iter 0)))
       ;; 打印 6
       (print (iter-next iter))
       ;; 打印 9
       (print (iter-next iter 8))
       ;; 打印 1; 这里 iter 和 iter2 的状态时分离独立的
       (print (iter-next iter2 nil))
       ;; 这里我们知道 iter 迭代序列已经到头了
       (condition-case x
           (iter-next iter)
         (iter-end-of-sequence
           ;; 打印 -1, 这里是my-iter 的常规返回值
           (print (cdr x)))))

最后更新于