27

ここに示すように、yield キーワードを使用して単純なステート マシンを実装することは可能ですか。私には、C# コンパイラが、yield ステートメントを機能させるためにステート マシンを内部的に実装しているため、大変な作業を行ったように見えます。

コンパイラーが既に行っている作業に便乗して、コンパイラーにステートマシンのほとんどを実装してもらうことはできますか?

誰かがこれを行ったことがありますか、技術的に可能ですか?

4

4 に答える 4

52

実現可能ですが、それは悪い考えです。イテレータ ブロックは、ステート マシンの実装に関する一般的な問題を解決するためではなく、コレクション用のカスタム イテレータを記述できるようにするために作成されました。

ステート マシンを作成する場合は、ステート マシンを作成するだけです。それは難しいことではありません。多くのステート マシンを記述したい場合は、ステート マシンをきれいに表現できる便利なヘルパー メソッドのライブラリを記述してから、そのライブラリを使用します。しかし、たまたまステートマシンを実装の詳細として使用する、まったく別のものを意図した言語構造を悪用しないでください。これにより、ステート マシン コードの読み取り、理解、デバッグ、保守、および拡張が難しくなります。

(ちなみに、私はあなたの名前を読んで二重に考えました。C# の設計者の 1 人は、Matt Warren という名前でもあります!)

于 2009-07-28T16:23:14.827 に答える
8

はい、それは絶対に可能であり、簡単に行うことができます。制御フロー コンストラクト ( forforeachwhile、... goto(goto特にこのシナリオに適しています ;))) をyields と共に使用して構築することができます。

IEnumerator<State> StateMachine
             (Func<int> currentInput /* gets current input from IO port */, 
              Func<int> currentOutput) {
    for (;;)  {
       if ((currentInput() & 1) == 0) 
           yield return new State("Ready"); 
       else {
           if (...) {
               yield return new State("Expecting more data");
               SendOutput(currentOutput());
               while ((currentInput() & 2) != 0) // while device busy
                    yield return new State("Busy");
           else if (...) { ... } 
       }
    }
}

// consumer:
int data;
var fsm = StateMachine(ReadFromIOPort, () => data);
// ...
while (fsm.Current != "Expecting more data")
    fsm.MoveNext();
data = 100;
fsm.MoveNext();
于 2009-07-28T15:22:24.773 に答える
4

イテレータブロックは確かにステートマシンを実装しますが、トリッキーなビットは次の入力を取得しています。次にどこに移動するかをどうやって知るのですか?ある種の共有された「現在の遷移」変数を持つことができると思いますが、それはやや厄介です。

入力が必要ない場合(たとえば、ステートマシンが状態間を循環している場合)は簡単ですが、それは興味深い種類ではありません:)

興味のあるステートマシンの種類を教えてください。

于 2009-07-28T15:24:14.847 に答える
4

これは古典的な意味でのステート マシンではありませんが、イテレータ ベースのマイクロ スレッディングに関する記事では、状態ベースのアクションに yield を独創的に使用しています。

IEnumerable Patrol ()
{
    while (alive){
        if (CanSeeTarget ()) {
            yield return Attack ();
        } else if (InReloadStation){
            Signal signal = AnimateReload ();
            yield return signal;
        } else {
            MoveTowardsNextWayPoint ();
            yield return TimeSpan.FromSeconds (1);
        };
    }
    yield break;
}
于 2010-04-08T00:14:53.513 に答える