ジェネレーター(generator)とは、潜在的に無限な値ストリームを生成する関数です。毎回その関数が値を生成するごとに、呼び出し側が次の値を要求するまで、自身をサスペンドします。
iter-defun
はジェネレーター関数を定義する。ジェネレーター関数は通常の関数と同様のsignatureをもつが、異なるように機能する。ジェネレーター関数は呼び出し時にbodyを実行するのではなく、かわりにiteratorオブジェクトをリターンする。このiteratorは値を生成するために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
がジェネレーター関数内部で出現した際には、カレントiteratorが一時停止してiter-next
からvalueをリターンすることを示す。iter-yield
は、次回iter-next
呼び出しのvalue
パラメーターへと評価される。
iter-yield-from
はiteratorが生成するすべての値を生成して、そのiteratorのジェネレーター関数が通常リターンする値へと評価される。これが制御を得ている間、iteratorはiter-next
を使用して送信された値を受け取る。
ジェネレーター関数を使用するには、まずそれを普通に呼び出してiteratorオブジェクトを生成します。iteratorはジェネレーターの固有のインスタンスです。その後でこのiteratorから値を取得するためにiter-next
を使用します。iteratorから取得する値がなくなると、iter-next
はそのiteratorの最終値とともにiter-end-of-sequence
のコンディションをraisesします。
ジェネレーター関数のbodyは、iter-next
の呼び出しの内側でのみ実行されることに注意することが重要です。iter-defun
で定義された関数の呼び出しはiteratorを生成します。何か興味があることが発生したら、iter-next
でこのiteratorを制御しなければなりません。ジェネレーター関数の個々の呼び出しは、それぞれが独自に状態をもつ別個のiteratorを生成します。
iteratorから次の値を取得する。(iteratorのジェネレーター関数がリターンしていて)生成される値が存在しない場合、iter-next
はコンディションiter-end-of-sequence
をシグナルする。このコンディションに関連付けられるデータ値は、iteratorのジェネレーター関数がリターンした値である。
valueはiteratorに送信されて、iter-yield
を評価した値になる。iteratorのジェネレーター関数の開始時には、ジェネレーター関数はiter-yield
フォームを何も評価していないので、与えられたiteratorにたいする最初のiter-next
呼び出しではvalueは無視される。
iteratorがunwind-protect
のbodyform
フォーム内でサスペンドされていたら、ガーベージコレクション処理後にEmacsが最終的にunwindハンドラーを実行する(unwind-protect
のunwindforms
内部ではiter-yield
は不当であることに注意)。その前に確実にこれらのハンドラーを実行するには、iter-close
を使用すること。
iteratorを簡単に連携できるように、便利な関数がいくつか提供されています:
iteratorが生成する各値をvarにバインドしつつbodyを実行する。
Common Lispのループ機能にはiteratorとともに機能も含まれています。Loop Facility in Common Lisp Extensionsを参照してください。
以下のコード片はiteratorとの連携における重要な原則を示すものです。
(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とiterは異なる状態をもつ (print (iter-next iter2 nil)) ;; ここでiterシーケンスの終了を待機 (condition-case x (iter-next iter) (iter-end-of-sequence ;; my-iterが通常の方法でリターンした-1をプリント (print (cdr x)))))