GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/elisp/Generators.html ・ emacs-doc.conao3.com

11.6 ジェネレータ

ジェネレータとは、潜在的に無限に続く値のストリームを生成する関数のことです。この関数は値を生成するたびに処理を一時停止し、呼び出し元が次の値を要求するのを待ちます。

マクロ: iter-defun name args [doc] [declare] [interactive] body…

iter-defun はジェネレータ関数を定義します。ジェネレータ関数は通常の関数と同じシグネチャを持ちますが、動作が異なります。呼び出された際に body を実行するのではなく、ジェネレータ関数はイテレータオブジェクトを返します。そのイテレータは body を実行して値を生成し、値を出力して iter-yield または iter-yield-from が現れる箇所で一時停止します。body が正常に終了すると、iter-next は body の結果を条件データとして iter-end-of-sequence に通知します。

body 内ではあらゆる種類の Lisp コードが有効ですが、iter-yield や iter-yield-from は unwind-protect フォーム内に記述することはできません。

マクロ: iter-lambda args [doc] [interactive] body…

iter-lambda は、iter-defun で生成されるジェネレータ関数と全く同様に動作する、名前のないジェネレータ関数を生成します。

マクロ: iter-yield value

ジェネレータ関数内で現れる場合、iter-yield は現在のイテレータが一時停止し、iter-next から value を返すことを示します。iter-yield は、iter-next への次回の呼び出しにおける value パラメータとして評価されます。

マクロ: iter-yield-from iterator

iter-yield-from yields all the values that iterator produces and evaluates to the value that iterator’s generator function returns normally. While it has control, iterator receives values sent to the iterator using iter-next.

ジェネレータ関数を使用するには、まず通常通りに呼び出してイテレータオブジェクトを生成します。イテレータはジェネレータの具体的なインスタンスです。次に、iter-nextを使用してこのイテレータから値を取得します。イテレータから取得する値がなくなると、iter-nextはイテレータの最終値とともにiter-end-of-sequenceコンディションを発生させます。

ジェネレータ関数の本体は、iter-next への呼び出し内でのみ実行される点に注意してください。iter-defun で定義された関数を呼び出すとイテレータが生成されます。何らかの処理を発生させるには、このイテレータを iter-next で駆動させる必要があります。ジェネレータ関数への呼び出しごとに異なるイテレータが生成され、それぞれが独自の状態を持ちます。

関数: iter-next iterator &optional value

iterator から次の値を取得します。生成すべき値がもうない場合(iterator のジェネレータ関数が値を返した場合)、iter-next が iter-end-of-sequence 条件を通知します。この条件に関連付けられたデータ値は、iterator のジェネレータ関数が返した値となります。

value はイテレータに渡され、iter-yield が評価される値となります。特定のイテレータに対する最初の iter-next 呼び出しでは、iterator のジェネレータ関数の開始時点ではジェネレータ関数がいかなる iter-yield 形式も評価していないため、value は無視されます。

関数: iter-close iterator

iterator が unwind-protect の bodyform 内でサスペンドされ、到達不能になった場合、Emacs は最終的にガベージコレクションのパス後にアンワインドハンドラを実行します。(なお、unwind-protect の unwindforms 内で iter-yield を使用することは不正です。)これらのハンドラがその前に確実に実行されるようにするには、iter-close を使用してください。

イテレータの操作を容易にするための便利な関数がいくつか用意されています:

マクロ: iter-do (var iterator) body …

iteratorが生成する各値にvarをバインドして、bodyを実行してください。

Common Lispのループ機構には、イテレータを扱うための機能も備わっています。Common Lisp ExtensionsのLoop Facilityを参照してください。

以下のコードは、イテレータを扱う際の重要な原則をいくつか示しています。

(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)))))