LoginSignup
12
14

More than 5 years have passed since last update.

例のキヨシチェックを Enumerator を使ってスマートに書いてみましょう

Posted at

こういう状態遷移が絡む系のチェックは Enumerator の仕組みを使うといい感じに書けそうですね。

というわけでできたもの

IEnumerator<string> RandomZunDoko()
{
    for (;;) {
        yield return Random.Range(0, 2) == 0 ? "ズン" : "ドコ";
    }
}

IEnumerator<string> KiyoshiCheck(IEnumerator<string> source)
{
    for (;;) {
        Retry:
        for (int i = 0; i < 4; ++i) {
            if (!source.MoveNext()) {
                yield break;
            }
            yield return source.Current;
            if (source.Current != "ズン") {
                goto Retry;
            }
        }
        if (!source.MoveNext()) {
            yield break;
        }
        yield return source.Current;
        if (source.Current != "ドコ") {
            goto Retry;
        }
        break;
    }
    yield return "キ・ヨ・シ!";
}

使い方

IEnumerator<V> SafetyLimit<V>(IEnumerator<V> source, int n)
{
    for (var i = 0; i < n; ++i) {
        if (source.MoveNext()) {
            yield return source.Current;
        } else {
            yield break;
        }
    }
}

IEnumerator<string> Counter(IEnumerator<string> source)
{
    var c = 0;
    while (source.MoveNext()) {
        yield return source.Current;
        ++c;
    }
    yield return string.Format("({0}回ズンドコしました)", c - 1);
}

string Result(IEnumerator<string> source)
{
    var s = "";
    while (source.MoveNext()) {
        s += source.Current;
        s += " ";
    }
    return s;
}

void Start()
{
    Debug.Log(Result(Counter(KiyoshiCheck(SafetyLimit(RandomZunDoko(), 1000)))));
}

結果

image

解説

RandomZundoko"ズン""ドコ" をランダムに無限に返し続ける Enumerator です。Enumerator は実は有限でななくても成り立ちます。

IEnumerator<string> RandomZunDoko()
{
    for (;;) {
        yield return Random.Range(0, 2) == 0 ? "ズン" : "ドコ";
    }
}

KiyoshiCheckstring を返す Enumerator source を受け取り、その内容を返しつつ、さらにズンドコがいい感じに続いた場合は "キ・ヨ・シ!" を返して完了する Enumerator です。

IEnumerator<string> KiyoshiCheck(IEnumerator<string> source)
{
    for (;;) {
        Retry:
        for (int i = 0; i < 4; ++i) {
            if (!source.MoveNext()) {
                yield break;
            }
            yield return source.Current;
            if (source.Current != "ズン") {
                goto Retry;
            }
        }
        if (!source.MoveNext()) {
            yield break;
        }
        yield return source.Current;
        if (source.Current != "ドコ") {
            goto Retry;
        }
        break;
    }
    yield return "キ・ヨ・シ!";
}

まず最初に 4 回連続ズンのチェックをします。sourceMoveNext() で一個進めて、Current で値を取ってきます。とりあえずそれを返します。そして、それが "ズン" だった場合はループを続けます。違った場合は、Retry: へ戻ります。これによって、最初から 4 連続ズンチェックがやり直されます。無事 for ループを抜けると、4 連続ズンが成立しことになり、次へ進みます。

こういうチェックをループでサクッと書けてしまうのは面白いですね。

for (int i = 0; i < 4; ++i) {
    if (!source.MoveNext()) {
        yield break;
    }
    yield return source.Current;
    if (source.Current != "ズン") {
        goto Retry;
    }
}

同様の原理で、次に "ドコ" が続いているかチェックします。

if (!source.MoveNext()) {
    yield break;
}
yield return source.Current;
if (source.Current != "ドコ") {
    goto Retry;
}
break;

合格すれば、"キ・ヨ・シ!" を返して終わります。

yield return "キ・ヨ・シ!";

ね、簡単でしょう?

順序通りにチェックが書けてしまうのがこのやり方の面白いところですね。今回は無かったですが、分岐が入ったりするとさらに威力を発揮します。皆さんも是非。

12
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
14