ジェネレータ (プログラミング)

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索

ジェネレータ: generator)は、計算機科学の分野において、繰り返し動作の振る舞いを制御するために用いられる特殊なサブルーチンである。ジェネレータは、パラメータを持ち、呼び出し可能で、値の列を持つという点で配列を返す関数と非常に良く似ている。しかし、すべての要素を一度に計算し配列を構築するのではなく、ジェネレータは一度に一つの値しか生成しない。これによりわずかしかメモリを消費せず、呼び出し側は最初の一部の値を用いて即座に処理を開始できる。そのため、ジェネレータの実装に遅延評価を用いることができる場合もある。しかし、ジェネエレータは参照透過ではない。簡単に言えば、ジェネレータは関数に類似しているが、イテレータのように振舞う。

目次

歴史 [編集]

ジェネレータは CLU 言語(1975年)で初めて登場し、[1] 現在は Python[2]C#JavaScript で利用できる。[3](CLU と C# では、ジェネレータはイテレータと呼ばれている) 。

ジェネレータはまた、文字列操作言語 Icon の特徴的な機能である。

使用法 [編集]

ジェネレータは一般的にループの中で呼び出される。ループ内でジェネレータの呼び出し部分に到達すると、最初にジェネレータルーチンの状態をカプセル化するイテレータオブジェクトがパラメータに対応する引数を用いて作成される。次に、ジェネレータの本体が、yield と呼ばれる特殊な操作が現れるまでイテレータのコンテキストで実行される。その時点で yield 操作によって提供される値が呼び出しの戻り値として用いられる。以降の繰り返し操作で次に同じジェネレータの呼び出しが行われたとき、ジェネレータ本体の実行は yield 操作以降に復帰し、次の yield 操作に遭遇するまで実行される。ジェネレータ本体の実行は、yield 操作に加えてジェネレータの呼び出しを含む最も内側にあるループが終了したときに finish 操作により終了される。

ジェネレータは yield される値を必要に応じてのみ計算するため、計算に時間がかかる数列や無限数列を表現するのに便利である。

擬似乱数発生器は、ジェネレータの一例である。

ジェネレータのある環境では、プログラミング言語の繰り返しループの構文は一つの loop ... end loop 構文に単純化できる。通常のいかなる繰り返しの構文も適切なジェネレータを正しく用いればシミュレートできる。

Python [編集]

Python のジェネレータの例:

def countfrom(n):
    while True:
        yield n
        n += 1
 
# Example use: 10 から 20 までの整数を表示する。
# countfrom() が無限ループで記述されているにも関わらず、
# 繰り返しが終了することに注意
 
for i in countfrom(10):
    if i <= 20:
        print i
    else:
        break
 
# もう一つのジェネレータ。必要に応じて素数をいくらでも作成する
 
def primes():
    n = 2
    p = []
    while True:
        if not any( n % f == 0 for f in p ):
            yield n
            p.append( n )
        n += 1
 
>>> f = primes()
>>> f.next()
2
>>> f.next()
3
>>> f.next()
5
>>> f.next()
7

上記の例は Python 2.5 以上か、NumPy モジュールの any() 関数を使用できる環境で動作する。

Python では、ジェネレータは状態が保存されたスタックフレームを保持するイテレータ と考えることができる。イテレータの next()メソッドがコールされると、Python は保存されたフレームを復帰し、次の yield 文に到達するまで実行する。ジェネレータのフレームは再び保存され、yield された値が呼び出し元に返される。

ジェネレータはより言語の表現に近い制御構造文として、たとえばコルーチンやファーストクラスの継続として実現することができる。 [4]

C# [編集]

C# 2.0 のジェネレータの例:

// 繰り返し可能な入力(おそらくは配列)を受け取り、るメソッド、
// すべて偶数を返すメソッド
public static IEnumerable<int> GetEven(IEnumerable<int> numbers)
{
    foreach (int i in numbers)
    {
        if ((i % 2) == 0)
        {
            yield return i;
        }
    }
}

複数の yield を返す命令を使うこともでき、その場合にはコンパイラーがそれらの命令の結果を各繰り返しで順次返却する:

public class CityCollection : IEnumerable<string>
{
   public IEnumerator<string> GetEnumerator()
   {
      yield return "New York";
      yield return "Paris";
      yield return "London";
   }
}

上記の例はいずれも Generics を使用しているが、これは必要ではない。yield のキーワードを使用するには、C# バージョン 2.0 以上が必要である。C# コンパイラの現在のバージョンは 3.5 である。

C++ [編集]

10 以上のカウントを行うジェネレータを(関数オブジェクトとして)定義し、11 回呼び出しを行って結果を表示する。

#include <iostream>
#include <iterator>
#include <algorithm>
 
class countfrom {
private:
  int count;
public:
  countfrom(int n) : count(n) {}
  int operator()() { return count++; }
};
 
int main() {
  std::generate_n(std::ostream_iterator<int>(std::cout, "\n"), 11, countfrom(10));
  return 0;
}

XL [編集]

XL では、イテレータが 'for' ループの基礎となっている:

import IO = XL.UI.CONSOLE
 
iterator IntegerIterator (var out Counter : integer; Low, High : integer) written Counter in Low..High is
    Counter := Low
    while Counter <= High loop
        yield
        Counter += 1
 
// 'var out' がイテレータ内で宣言されているため、ここで I を宣言する必要はない。
// 整数 I の暗黙の宣言が 1..5 ループ内で行われている。
for I in 1..5 loop
   IO.WriteLn "I=", I

PHP [編集]

PHP では、特に MySQL データベースと接続する場合、ジェネレータである組み込み関数 mysql_fetch_array を用いるのが便利であることが多い。

   $sql = 'SELECT * FROM `users` ORDER BY `user_id` DESC LIMIT 10';
   $res = mysql_query( $sql );
   // このジェネレータは返却するデータを保持しており、
   // 新しい値が返却され続ける限り while の条件が満たされ、返すデータがなければ偽となる。
   // ここで、等号は比較ではなく、代入の演算子である
   while ( $row = mysql_fetch_array( $res ) ) { 
       // process $row
   }

その他の実装 [編集]

Java は標準で継続の機能を持っていないため、バイトコード操作プログラム(特に、 WebObject's ASM)や Java 5 の新しい機能である VM Instrumentationを用いて この概念を実装しようとする試みが行われた。コードはここで公開されている。

使用例:

public static Iterable countFrom(final int n) {
  return new Yielder() {
    protected void yieldNextCore() {
      int times = n;
      while (times-- > 0) {
        yieldReturn(times);
      }
    }
  };
}

関連項目 [編集]

  • リスト内包(英語) 値の列を生成する別の構文
  • イテレータ 一度に一つのリスト要素を生成する概念
  • 遅延評価 必要なときに値を生成する
  • Corecursion(英語) yield の代わりに無限のデータを再帰で生成する
  • コルーチン サブルーチンをさらに一般化したもの
  • 継続 制御フローの一般化

参考文献 [編集]

  1. ^ Liskov, Barbara (1992年4月). “A History of CLU (pdf)”. 2008年3月8日閲覧。
  2. ^ Python Enhancement Proposals PEP 255: Simple Generators, PEP 289: Generator Expressions, PEP 342: Coroutines via Enhanced Generators
  3. ^ New In JavaScript 1.7”. 2006年10月10日閲覧。
  4. ^ Kiselyov, Oleg (2004年1月). “General ways to traverse collections in Scheme”. 2008年3月8日閲覧。
  • 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) [1]