見出し画像

Velo 第73回 ジェネレータ

前回は反復可能オブジェクトからイテレータメソッド[Symbol.iterator]( ) を使ってイテレータを作りましたが、ジェネレータ関数は関数でイテレータを定義します。
ジェネレータ関数で作られたイテレータをとくにジェネレータと言います。Veloで使う場面は殆どありませんが、イテレータつながりのお話です。


ジェネレータ関数

関数の宣言を function* で行うとジェネレータ関数が定義されます。
そしてそのように定義された関数を呼び出すと、戻り値はイテレータになります。
つまりジェネレータ関数とはイテレータを生成する関数です。このイテレータを特にジェネレータと呼びます。

一般の関数で戻り値を決めるのは return 文ですが、ジェネレータ関数で戻り値を決めるのは yield 文です。
ジェネレータは next( )メソッドが呼び出されると、ジェネレータ関数中の yield文で実行を中止し、そこでの値を value として返し、以後これを繰り返します。
ドミノ倒しのドミノが yield文の役割です。

先ず簡単な例を引きます。1、2、3を順に生成するジェネーレータ関数 threeNumbers( ) とジェネレータ numbers を以下の様に定義してみましょう。

function* threeNumbers(){yield 1; yield 2; yield 3};

const numbers = threeNumbers()

ジェネレータ numbers は next( )メソッドが実行されると、threeNumbers( ) 中で定義された yield文により、最初は1、次は2、最後に3を返し、そして終了します。

yield文に出会う度に値を返す仕組みをコーディングすることがジェネレータ関数では肝になります。
threeNumbers( ) はこの仕組みを最も単純な形で定義したジェネレータ関数です。実際 numbers をスプレッド演算子で展開すると以下の様になります。

numbers の展開

もう少し計算式を表に出したジェネレータ関数を以下に作って見ましょう。

series の展開

ここでは1から順に2倍していく数列をジェネレータ関数 numberSeries( ) で定義しています。
そしてそれを実行してジェネレータ series を作っています。このジェネレータは無限数列を定義しているのでスプレッド演算子で展開するとメモリーが枯渇するまで展開します。
そうならない様に for 文で5個まで展開させています。

yield演算子

ジェネレータ関数は、ジェネレータの next( )メソッドで計算を再開し、yield文での値を next( ) の戻り値として返し、そこで自身の計算を中断し次の再開に備えます。yield はジェネレータ関数の中でしか使えません。

yield は演算子の一つで yield a の結果は計算再開後の next(b) の引数 b になります。例えば以下のコーディングをご覧下さい。

test() 関数

yield 1 でジェネレータに値を戻した後にプログラムは中断されますので、yield 1に値が付与されるのは次の next('b') が実行されたときとなります。
その時に yield 1 は 'b' となり console.log(yield 1) の実行で'b'が表示されます。console.log('second')は問題なく表示され、次の yield 2でジェネレータに2を返して再びプログラムが中断されます。

従って、iter.next('a') では yield 1に 'a' が与えられず、console.log(yield 1) も実行されません。
そして、iter.next('b') の実行で初めて yield 1 に 'b' が与えられ、b、second、{value: 2, done: false} が順に出力されます。

Velo開発のご依頼はこちら

いいなと思ったら応援しよう!