コルーチンの話を進める上でどうしてもC#のyieldを理解してもらわなくてはいけません。
凪瀬さんも少し誤解されているように感じました。
ここで少し解説したいと思います。
●実は何も返してないyield return
ご存じなければ驚くかもしれませんが
yield returnは正確には何も返していません。
まずMoveNextの返値ではありません。
MoveNextの返値はMoveNextの成功可否をboolで返します。
つまり列挙が継続可能かを返します。
ではyield returnは何を返しているかというと、
IEnumeratorインターフェースのCurrentプロパティの内容を更新しています。
言語構文上returnという単語を用いているだけなのです。なので「何か返している」という誤解が生じているように思います。
using System;
using System.Collections;
namespace CoRoutine
{
static class Program
{
[STAThread]
static void Main(string[] args)
{
IEnumerator e = TaskA();
for (int i = 0; i < 10; i++) {
bool b = e.MoveNext();
Console.WriteLine(e.Current);
}
}
static IEnumerator TaskA()
{
while (true) {
for (int i = 1; i <= 3; i++) {
yield return i;
}
}
}
}
}
●Currentで表現できる状態変化
具体的なゲームの話をしましょう。
「男の子がまばたきしている」
ことを考えます。
Boyには状態を変化させるUpdateメソッドと
現状を画面へ表示するRenderメソッドがあります。
Boy boy = new Boy();
while (boy.生きている間)
{
boy.Update();
boy.Render();
}
Updateメソッドの実装は次のようになるでしょう。
3項演算を使えばもっとシンプル化も可能です。
void Update()
{
if (this.eye == CLOSE)
this.eye = OPEN;
else
this.eye = CLOSE;
}
ここでは目というプロパティに値を代入することで
状態変化を表現しています。
これはyield構文を使うとこのように書けるでしょう。
class Boy
{
IEnumerator enumerator;
public Boy()
{
enumerator = GetEnumerator();
}
void Update()
{
enumerator.MoveNext();
eye = enumerator.Current;
}
IEnumerator GetEnumerator()
{
while (true) {
yield return OPEN;
yield return CLOSE;
}
}
}
●Currentでは表現できないケース
さて別の実装を考えます。
「女の子が携帯メールを打ちながら笑いながら歩いている」
ことを考えます
GirlはBoyと同じく状態を変化させるUpdateメソッドと
現状を画面へ表示するRenderメソッドがあります。
メインループはBoyと同じなので省略します。
yieldを使わない場合、今回のUpdateメソッドはこのように
なっているとします。
void Update()
{
// メール内容を追加する
string s = GetNextMailSentence();
this.phone.Add(s);
// 歩く
MoveStep();
}
状態変化はすべてメソッドによって実装されています。
この場合、反論はあるかも知れませんが私はyieldの実装はこのようにするのを理想と考えています。
class Girl
{
IEnumerator enumerator;
public Girl()
{
enumerator = GetEnumerator();
}
void Update()
{
enumerator.MoveNext();
}
IEnumerator GetEnumerator()
{
while (true) {
// メール内容を追加する
string s = GetNextMailSentence();
this.phone.Add(s);
// 歩く
MoveStep();
yield return 0;
}
}
}
●私の見解
状態変化が1つのプロパティの代入による場合、
yield returnの返値としてそれを表現することができます。
しかし現実には
・複数のプロパティによって表現された状態
・代入で表現できない状態変化
などは多くありますので一般論的にみるとyield returnによる一つの値の上書きには意味を持たせる必然性は全くないと私は考えます。
むしろyieldを思想として列挙を考えていない別言語のyieldになぞらえて使用しているのである、という意思表示のためにもコルーチンとしてのyieldは無意味な値を返すべきだと考えます。
それはコルーチン向けの言語で返値が存在していない理由の一つだとこじつけられそうにも思います。