ECMAScript 2015のジェネレータ関数とyield式を使うと、C#の反復子ブロックとyield return文と似た形ですっきりと反復処理を記述できる。
powered by Insider.NET
前回まではECMAScript 2015(以下、ES2015)のPromiseオブジェクトと、次期ECMAScriptで導入予定のasync/awaitキーワードを使った非同期処理の記述方法を見た。今回はES2015で導入されたジェネレータ関数とこれに関する幾つかの構文要素を紹介する。ジェネレータ関数を使うと、C#でいうところの反復子(イテレータ)と同様な記述が行えるようになる。
C#の反復子(イテレータ)とは、「yield return」文および「yield break」文を本体に含んだメソッドやプロパティ(getter)のことで、コレクションを反復的に処理するために使用する(というのは、本フォーラムの読者の方ならよくご存じのことだろう)。
以下に簡単な例を示す。C#では反復子(iterator)という呼び方が一般的だが、ここではES2015のジェネレータ関数に合わせてsampleGeneratorというメソッドを作成している。
static IEnumerable<int> sampleGenerator(int from, int to)
{
while(from <= to)
{
yield return from++;
}
}
static void Main(string[] args)
{
foreach (var i in sampleGenerator(1, 5))
{
Console.WriteLine(i);
}
Console.ReadKey();
}
sampleGeneratorメソッドを呼び出しても、その本体がすぐに実行されるのではなく、その内容をカプセル化したオブジェクトが返される(メソッドの戻り値の型によって、これはIEnumerator/IEnumerator<T>を実装する「列挙子オブジェクト」か、IEnumerable/Enumerable<T>を実装する「列挙可能なオブジェクト」のいずれかとなる)。その後、列挙が行われるたびに、カプセル化されたメソッド内の「yield return」文まで実行され、その値と共に制御が呼び出し側にいったん戻される(「yield break」文が実行されると、そこで反復子の実行は終了する)。
上のコード例であれば、foreach文でまず「列挙可能なオブジェクト」が作成され、その後、列挙が行われるたびに列挙可能なオブジェクトによってカプセル化されたsampleGeneratorメソッドの「yield return from++;」行までが実行される。この行によって返された値がMainメソッドの変数iに代入され、それがコンソールに出力されると、再度、列挙が行われ……といった処理を反復子が終了するまで繰り返される。
ES2015では、ジェネレータ関数により、上記と同様なコードを記述できる。
ジェネレータ関数を定義するには「function」キーワードに続けて「*」を付加する。以下に「function*」宣言と「function*」式の大ざっぱな構文を示す。
// funtion*宣言によるジェネレータ関数の定義
function* 関数名(パラメーターリスト) {
実行する処理(yieldを含む)
}
// function*式によるジェネレータ関数の定義
var 変数名 = function* (パラメーターリスト) {
実行する処理(yieldを含む)
}
以下に上記のC#コードと同様な処理を行うES2015コードを示す。
function* sampleGenerator(from, to) {
while(from <= to)
yield from++;
}
for (var i of sampleGenerator(1,5)) {
console.log(i);
}
C#とは異なり「yield return」ではなく「yield」に続けて、呼び出し側に戻す値を記述している。また、forループでは「in」ではなく「of」が使われている点にも注意してほしい(for-of文。後述)。
sampleGenerator関数を呼び出しても、その本体がすぐに実行されるのではなく、その内容をカプセル化したイテレータオブジェクトが返される。その後、列挙が行われるたびにカプセル化されたメソッド内の「yield」式まで実行され、その値と共に制御が呼び出し側にいったん戻される。
上のコード例であれば、for-of文でまずイテレータオブジェクトが作成され、その後、列挙が行われるたびにカプセル化されたsampleGenerator関数の「yield from++;」行までが実行される。この行によって返された値が変数iに代入され、それがコンソールに出力されると、再度、列挙が行われ……といった処理を反復が終了するまで繰り返される(この辺のロジックはC#コードの説明で既に話した通りだ)。
for-ofループを使えば、イテレータオブジェクトの処理を隠蔽し、反復処理をスマートに記述できるが、次ページではこのオブジェクトについて少し詳しく見てみよう。
Copyright© Digital Advantage Corp. All Rights Reserved.