Next: , Previous: , Up: Control Structures   [Contents][Index]


11.6 ジェネレーター

ジェネレーター(generator)とは、潜在的に無限な値ストリームを生成する関数です。毎回その関数が値を生成するごとに、呼び出し側が次の値を要求するまで、自身をサスペンドします。

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

iter-defunはジェネレーター関数を定義する。ジェネレーター関数は通常の関数と同様のsignatureをもつが、異なるように機能する。ジェネレーター関数は呼び出し時にbodyを実行するのではなく、かわりにiteratorオブジェクトをリターンする。このiteratorは値を生成するためにbodyを実行、値を発行するとiter-yielditer-yield-fromが出現するまで一時停止する。bodyが正常にリターンした際に、iter-nextがコンディションデータとなるbodyの結果とともに、iter-end-of-sequenceをシグナルする。

body内部では任意の種類のLispコードが有効だが、iter-yielditer-yield-fromunwind-protectフォームの内部にあってはならない。

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

iter-lambdaiter-defunで生成されたジェネレーター関数と同様な、無名のジェネレーター関数を生成する。

Macro: iter-yield value

iter-yieldがジェネレーター関数内部で出現した際には、カレントiteratorが一時停止してiter-nextからvalueをリターンすることを示す。iter-yieldは、次回iter-next呼び出しのvalueパラメーターへと評価される。

Macro: iter-yield-from iterator

iter-yield-fromiteratorが生成するすべての値を生成して、そのiteratorのジェネレーター関数が通常リターンする値へと評価される。これが制御を得ている間、iteratoriter-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を生成します。

Function: iter-next iterator value

iteratorから次の値を取得する。(iteratorのジェネレーター関数がリターンしていて)生成される値が存在しない場合、iter-nextはコンディションiter-end-of-sequenceをシグナルする。このコンディションに関連付けられるデータ値は、iteratorのジェネレーター関数がリターンした値である。

valueはiteratorに送信されて、iter-yieldを評価した値になる。iteratorのジェネレーター関数の開始時には、ジェネレーター関数はiter-yieldフォームを何も評価していないので、与えられたiteratorにたいする最初のiter-next呼び出しではvalueは無視される。

Function: iter-close iterator

iteratorunwind-protectbodyformフォーム内でサスペンドされていたら、ガーベージコレクション処理後にEmacsが最終的にunwindハンドラーを実行する(unwind-protectunwindforms内部ではiter-yieldは不当であることに注意)。その前に確実にこれらのハンドラーを実行するには、iter-closeを使用すること。

iteratorを簡単に連携できるように、便利な関数がいくつか提供されています:

Macro: iter-do (var iterator) body …

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