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

  • ジェネレータ

    各種スクリプト言語で、概ね「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の際、ジェネレータがちゃんとリセット(再び最初から実行)されていることに注目してください。