yield文

(式と演算子)
https://piro.sakura.ne.jp/latest/blosxom/webtech/javascript/2006-08-07_yield.htm

JavaScriptと同じくスクリプト言語のPythonから持ち込まれた仕組み。関数内での使い方としてはreturn文の仲間に近い。
return文は関数の実行をその場で終了して、引数として与えられた変数の値を返すというもの。「yield文が登場する関数」の中から見たら、yieldもそれと似たような動作をする、すなわち「yield文に引数として渡した変数の値が返される」ということ。
yield文がreturn文と違うのは、yield文が登場する関数の外から見たときの挙動である。
return文の場合、return文が含まれる関数を実行すると、return文に渡された変数の値が返ってくる。

function test() {
  var value = 'hoge';
  return value;
}
alert(test()); // "hoge"と表示される

yield文の場合、yield文が含まれる関数を実行しても、yield文に渡された変数の値は返ってこない。何が返ってくるかというと、「ジェネレータ」と呼ばれる特殊なオブジェクトが返ってくる。

function test() {
  var value = 'hoge';
  yield value;
}
alert(test()); // "[object Generator]"と表示される

ジェネレータとはnext()というメソッドを持つオブジェクトの型で、イテレータの一種である。

イテレータとは、内部に複数の要素を持つ配列と「どの要素にフォーカスしているか」の情報を保持しているようなオブジェクトで、next()メソッドを実行するたびに、「現在フォーカスしている要素」を返しつつ内部のカウンタを一つ進める、というふうな振る舞いを見せるオブジェクトのデザインパターンの一種である。
例えば以下のようなものもイテレータと言える。

var iterator = {
  next: function () {
    if (this.count >= this.elements.length) {
      throw "もう全部見たよ!";
    }
    return this.elements[this.count++];
  },
  count: 0,
  elements: ["hoge", "hage", "foobar"],
};
alert(iterator.next());
alert(iterator.next());
alert(iterator.next());

ちなみにイテレータで最後の要素まで参照し終えた後でさらに次の要素を参照すると、普通はエラーになる。上の例ではthrow文でそれを再現している。

内部でyield文を使った関数は、ただの関数ではなくてジェネレータを生成するための特殊な関数(ファクトリー)という扱いになる。この関数を実行すると、返り値はジェネレータオブジェクトとなる。よって、実際に使うときは以下の例のようになる。

function test() {
  var value = 'hoge';
  yield value;
}
var generator = test();
alert(generator.next()); // "hoge"と表示される

next()というメソッドの定義はどこにも書いた覚えがないのに、勝手にそういうものが生成される。強いて言うなら、yield文を含んだ関数の定義内容がそのままnext()メソッドの内容になる、といった感じである。

ジェネレータを実際に動かすときに、yieldとreturnのもう一つの違いがはっきり出てくる。return文はそこで関数の実行を強制的に終了させてしまう。それに対してyield文は、yield文が実行されたとき、渡された引数の値を返すと同時に、ビデオの「一時停止」ボタンを押したようにその行で処理を一時停止する。そして、次にnext()メソッドが呼ばれたときに、その次の文から処理を再開する。(そして、次のyield文に辿り着いた所でまた一時停止して、値を返す。)一時停止してから再開するまでの間、処理の状態や変数の内容も完全に保持される。関数にとっての「一時停止」文としても作用するというわけである。

function test() {
  var i = 0;
  yield 'hoge ' + (i++);
  yield 'hage ' + (i++);
  yield 'foobar ' + (i++);
}
var generator = test();
alert(generator.next()); // "hoge 0"
alert(generator.next()); // "hage 1"
alert(generator.next()); // "foobar 2"
alert(generator.next()); // これはエラーになる

yieldとジェネレータの機能の副産物として、これらを使うと処理のスレッド化(適当なところで処理を一時停止して、他の処理に制御を移し、そっちの処理もまた適当なところで一時停止して、また一番最初の処理に戻ってくる、という感じのこと)もできる。

inserted by FC2 system