ジェネレータの概要
ジェネレータは、プログラムにおいて連続的に
数列の要素や他の値を生成し、異なる手続きにそれを渡す機能を持つ手続きです。呼び出されるたびに異なる値を返す関数として実装されることが一般的です。また、コールバックの形で別の手続きを呼び出す方法も存在します。これにより、データ生成や処理の流れを効率良く管理することが可能になります。
性質
ジェネレータは通常、参照透過ではありません。これは、呼び出しのたびに異なる出力を生成するためです。ジェネレータは、コンテナに含まれる要素それぞれに対して実行される
イテレータの一種と言われています。具体的な実装方法には、
コルーチンや`call/cc`、マルチスレッドなどがあり、使用するプログラミング言語によって実装の詳細は異なる場合があります。ジェネレータの一例として、
擬似乱数発生器が挙げられます。ただし、`yield`というキーワードが使われているからといって必ずしもジェネレータに該当するわけではありません。この点に関しては注意が必要です。
歴史
ジェネレータの概念は、1975年に発表されたCLUという言語から始まりました。その歴史において、CLUの
イテレータはAlphardという別のプログラミング言語の構成要素の影響を受けています。Alphardにおけるジェネレータは、IPL-Vに起源を持ち、リストに対して特定の操作を繰り返し行う高階関数に類似した機能を持っていました。リストの各要素に手続きを適用し、新たなリストを生成するというプロセスが、ジェネレータの本質とつながっています。これ以外にも、
Icon、
Python、
JavaScriptなどのプログラミング言語においても、ジェネレータは別の形で実装されています。
実装例
Pythonにおいては、関数定義内に`yield`文が存在する場合、通常の関数ではなく、
コルーチンとして振る舞う構造になります。このような関数は、
イテレータとしてのインタフェースを持つオブジェクトを返します。一般に、`yield`文を持つ関数をジェネレータと呼び、この関数が返すオブジェクトを
イテレータと定義することができます。
イテレータの内部では、各`yield`文までの実行状態を保存するスタックフレームが保持され、`next()`関数が呼び出されると、その状態に復帰し次の`yield`に到達するまで実行が続行されます。`yield`文が実行されることで、現在のフレームが保存され、引数として与えられた値が呼び出し元に返されます。
この機能は
Python 2.5以降、もしくは
NumPyモジュールの`any()`を使用できる環境で利用可能です。
Scheme言語においては、単純な
遅延評価の手法を用いてジェネレータを実装することができます。また、
継続を使用した実装のサンプルも存在しています。
C#
C#でも`yield`キーワードが使用されます。`yield`ステートメントが含まれるブロックは、反復子(
イテレータ)として認識され、これがジェネレータ自身となります。反復子メソッドの戻り値としては、`IEnumerable`や`IEnumerator`、またはそのジェネリックバージョンである`IEnumerable
`や`IEnumerator`を指定することができます。ここでの`yield return`は値を生成し、`yield break`は生成の終了を意味します。これによって、効率的にデータの生成と処理が行えるようになっています。
参考文献
- - Stephan Murer, Stephen Omohundro, David Stoutamire and Clemens Szyperski: Iteration abstraction in Sather. ACM Transactions on Programming Languages and Systems, 18(1):1-15 (1996)