pythonなどのように、JavaScriptでもES6からGeneratorが使えるようです。
Generatorとは
呼び出すたびに値やオブジェクトを生成して返す仕組み。
Generatorを使うことで、オブジェクトや配列値の遅延評価を行うことができます。
遅延評価とは、必要なタイミングになるまで値の評価を遅延させることです。
この遅延評価は協力で、例えば無駄にオブジェクトをプールさせることなく、必要なときにジェネレータを呼び出してオブジェクトを生成、といったことができるようになります。
Generatorの使い方
Generatorを使って実現可能な概念の一つに、無限数列があります。
今回は要素が0を含む正の整数からなる無限配列を例として作ってみました。
function *infinityArrayGenerator(){ var i = 0; while (true) { yield i++; } } var infinityArray = infinityArrayGenerator(); console.log(infinityArray.next().value); // 0 console.log(infinityArray.next().value); // 1 console.log(infinityArray.next().value); // 2 console.log(infinityArray.next().value); // 3
Generatorの生成
function *infinityArrayGenerator(){ var i = 0; while (true) { yield i++; } }
infinityArrayGenerator
はジェネレータです。
関数名の頭に*(アスタリスク)を付加することで、関数がGeneratorであることを表します。
Generatorではyield
キーワードを用います。Generatorではこのyieldが呼ばれた行で一旦値を評価して返し、Generatorが再度呼び出されるとこの地点から再び処理が再開されます。
var infinityArray = infinityArrayGenerator(); console.log(infinityArray.next().value); // 0 console.log(infinityArray.next().value); // 1 console.log(infinityArray.next().value); // 2 console.log(infinityArray.next().value); // 3
上のコードではinfinityArray
にジェネレータオブジェクトを格納し、
next()
を呼ぶことでGeneratorに値の「生成」を行わせています。
next()
を呼ぶたびにinfinityArray
内ではyield
時点で処理が一旦中断され、その時点でのi
の値が返されます。
なぜGeneratorを使うのか
先程も述べましたが、Generatorの強みは遅延評価が可能な点にあります。
上記の例ではnext()
を読んで初めて整数の生成が行われています。逆に言うと、next()
が呼ばれるまでは値が生成されていません。infinityArray
は無限の整数列として振る舞うことができるにもかかわらず、それに必要なメモリ量を(現時点では)食いつぶしてはいません。
このように、「必要なときに必要なだけ」値・オブジェクトの生成を行ってくれるのがGeneratorの強みです。
(厳密にはメモリ容量もストレージ容量も有限なので、infinityArrayは無限になりませんが・・・)
おまけ
nextの引数に値を渡すと、渡した値をyield式の返戻値とすることができます。
function *infinityArrayGenerator(){ var i = 0; while (true) { // nextに値を設定すればiに+100、デフォルトで+1 var value = yield i ; i += value || 1; } } var infinityArray = infinityArrayGenerator(); console.log(infinityArray.next().value); // 0 console.log(infinityArray.next().value); // 1 console.log(infinityArray.next(100).value); // 101 console.log(infinityArray.next().value); // 102