~文法メモ~ イテレータとジェネレータ

ジェネレータ

各種スクリプト言語で、概ね「yield」というキーワードがあれば、
同様の機能があると思われます。

各種言語で概ね共通したイメージとしては、
・関数を呼び出した時に、「yield文(や関数)」まで実行。
・該当関数の処理は一端停止しつつ、yieldの引数(等)の値を返す。
・次に同じ関数を呼び出した際には、一端停止したのを解除して、その続き(のステップ/行)から処理を再開

という感じです。
ECMAScript6では「yield演算子」という名称となっています。

yieldの一番簡単な例

function* sampleGenerator(i) {
  yield i + 1; // ここで i+1を返して、関数は(次の呼び出しまで)一端停止
  yield i + 2; // ここで i+2を返して、関数は(次の呼び出しまで)一端停止
  yield i + 3; // ここで i+2を返して、関数は(次の呼び出しまで)一端停止
}

var gen = sampleGenerator(10);

hm.debuginfo(gen.next().value); // 10
hm.debuginfo(gen.next().value); // 11
hm.debuginfo(gen.next().value); // 12

yieldの単純な例

function* idMaker(){
    var index = 0;
    while(true) {
        yield index++;
    }
}

var gen = idMaker();

hm.debuginfo(gen.next().value); // 0
hm.debuginfo(gen.next().value); // 1
hm.debuginfo(gen.next().value); // 2

yieldの中で別のyieldを使う

yieldの中で別のyieldを使用する場合には、
「yield*」といった独特の表記となります。

function* anotherGenerator(i) {
  yield i + 1;
  yield i + 2;
  yield i + 3;
}
function* generator(i){
  yield i;
  yield* anotherGenerator(i);
  yield i + 10;
}

var gen = generator(10);

hm.debuginfo(gen.next().value); // 10
hm.debuginfo(gen.next().value); // 11
hm.debuginfo(gen.next().value); // 12
hm.debuginfo(gen.next().value); // 13
hm.debuginfo(gen.next().value); // 20

yieldが返しているもの

「yield」(より厳密には「yield.next()」)が返しているものは、
・{ value : 今回の値, done:false }
もしくは、
・{ done:true }
です。

function* sampleGenerator() {
  yield "あ"
  yield "い"
  yield "う"
}

var gen = sampleGenerator();

hm.debuginfo(gen.next()); // {"value":"あ","done":false}
hm.debuginfo(gen.next()); // {"value":"い","done":false}
hm.debuginfo(gen.next()); // {"value":"う","done":false}
hm.debuginfo(gen.next()); // {"done":true}

for...ofなどによるジェネレータのループ

「for...of」先述した仕組みで「done:true」が出現するまで、
1つ1つ要素を出すといったことに対応しているため、以下のような順次処理が可能です。

function* sampleGenerator() {
  yield "あ"
  yield "い"
  yield "う"
}


for (let e of sampleGenerator()) {
    hm.debuginfo(e);
}
// "あ"
// "い"
// "う"

for (let e of sampleGenerator()) {
    hm.debuginfo(e);
}
// "あ"
// "い"
// "う"

2つ「for...of」があります。
2番めのfor...ofの際、ジェネレータがちゃんとリセット(再び最初から実行)されていることに注目してください。