ジェネレータとは、潜在的に無限に続く値のストリームを生成する関数のことです。この関数は値を生成するたびに処理を一時停止し、呼び出し元が次の値を要求するのを待ちます。
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 は、iter-defun
で生成されるジェネレータ関数と全く同様に動作する、名前のないジェネレータ関数を生成します。
ジェネレータ関数内で現れる場合、iter-yield は現在のイテレータが一時停止し、iter-next から value
を返すことを示します。iter-yield は、iter-next への次回の呼び出しにおける value
パラメータとして評価されます。
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 で駆動させる必要があります。ジェネレータ関数への呼び出しごとに異なるイテレータが生成され、それぞれが独自の状態を持ちます。
iterator から次の値を取得します。生成すべき値がもうない場合(iterator のジェネレータ関数が値を返した場合)、iter-next が iter-end-of-sequence 条件を通知します。この条件に関連付けられたデータ値は、iterator のジェネレータ関数が返した値となります。
value はイテレータに渡され、iter-yield
が評価される値となります。特定のイテレータに対する最初の iter-next 呼び出しでは、iterator のジェネレータ関数の開始時点ではジェネレータ関数がいかなる iter-yield 形式も評価していないため、value
は無視されます。
iterator が unwind-protect の
bodyform 内でサスペンドされ、到達不能になった場合、Emacs
は最終的にガベージコレクションのパス後にアンワインドハンドラを実行します。(なお、unwind-protect の unwindforms
内で iter-yield
を使用することは不正です。)これらのハンドラがその前に確実に実行されるようにするには、iter-close を使用してください。
イテレータの操作を容易にするための便利な関数がいくつか用意されています:
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)))))