11.6 生成器

生成器(generator) 是一种能够生成潜在无限值序列的函数。该函数每生成一个值后,会暂停自身执行,等待调用方请求下一个值。

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

iter-defun 用于定义一个生成器函数。生成器函数的签名与普通函数一致,但执行逻辑不同:调用生成器函数时,并不会立即执行 body(函数体),而是返回一个迭代器对象。这个迭代器会运行 body 来生成值 —— 每当执行到 iter-yielditer-yield-from 处,迭代器会输出一个值并暂停执行。当 body 正常返回时,iter-next 会触发 iter-end-of-sequence 信号,并将 body 的返回结果作为该信号的条件数据。

body 中可以编写任意合法的 Lisp 代码,但 iter-yielditer-yield-from 不能出现在 unwind-protect 形式内部。

Macro: iter-lambda args [doc] [interactive] body…

iter-lambda 用于创建一个匿名生成器函数,其工作方式与通过 iter-defun 创建的生成器函数完全一致。

Macro: iter-yield value

iter-yield 出现在生成器函数内部时,它会指示当前迭代器暂停执行,并将 value 作为 iter-next 的返回值。而 iter-yield 本身的求值结果,则是下一次调用 iter-next 时传入的 value 参数。

Macro: iter-yield-from iterator

iter-yield-from 会产出 iterator(迭代器)生成的所有值,其自身的求值结果为 iterator 对应的生成器函数正常返回的值。在 iter-yield-from 掌控执行流程期间,iterator 会接收所有通过 iter-next 发送给该迭代器的值。

使用生成器函数的步骤:首先以常规方式调用生成器函数,得到一个 迭代器(iterator) 对象(迭代器是生成器的一个具体实例);然后通过 iter-next 从该迭代器中获取值。当迭代器中没有更多值可供获取时,iter-next 会抛出 iter-end-of-sequence 条件异常,并将迭代器的最终值作为该异常的附加数据。

需要特别注意的是,生成器函数体仅会在调用 iter-next 时执行。调用通过 iter-defun 定义的函数会生成一个迭代器;只有通过 iter-next 驱动这个迭代器,才会触发有实际意义的执行逻辑。 每次调用生成器函数,都会生成一个 独立 的迭代器,且每个迭代器都拥有自身的状态。

Function: iter-next iterator &optional value

iterator(迭代器)中获取下一个值。若已无更多可生成的值(即迭代器对应的生成器函数已返回),iter-next 会触发 iter-end-of-sequence 条件异常;该异常关联的附加数据,即为迭代器 iterator 对应的生成器函数的返回值。

value 会被传入迭代器,并成为 iter-yield 的求值结果。对于某个迭代器的第一次 iter-next 调用,value 会被忽略 ——因为在迭代器对应的生成器函数启动之初,生成器函数尚未执行到任何 iter-yield 形式。

Function: iter-close iterator

iterator(迭代器)暂停于 unwind-protectbodyform(主体代码)内部,且变得无法被访问,Emacs 会在一次垃圾回收过程后最终执行 unwind 处理函数。(注意:iter-yieldunwind-protectunwindforms(清理代码)内部是不合法的。)如需确保这些处理函数在此之前执行,可调用 iter-close

Emacs 提供了一些便捷函数来简化迭代器的使用:

Macro: iter-do (var iterator) body …

依次将 var 绑定为 iterator 生成的每个值,并执行 body(代码体)。

Common Lisp 循环工具也包含用于操作迭代器的特性,see Loop Facility in Common Lisp Extensions

以下代码片段演示了使用迭代器的一些核心原则。

(require 'generator)
(iter-defun my-iter (x)
  (iter-yield (1+ (iter-yield (1+ x))))
   ;; Return normally
  -1)

(let* ((iter (my-iter 5))
       (iter2 (my-iter 0)))
  ;; Prints 6
  (print (iter-next iter))
  ;; Prints 9
  (print (iter-next iter 8))
  ;; Prints 1; iter and iter2 have distinct states
  (print (iter-next iter2 nil))

  ;; We expect the iter sequence to end now
  (condition-case x
      (iter-next iter)
    (iter-end-of-sequence
      ;; Prints -1, which my-iter returned normally
      (print (cdr x)))))

emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike