3

私は次のオプションを思いついた:

gotoステートメントの使用:

Start:
    goto Data
Data:
    goto Finish
Finish:
    ;

switchステートメントの使用:

switch(m_state) {
    case State.Start:
        m_state = State.Data;
        break;
    case State.Data:            
        m_state = State.Finish;
        break;
    case State.Finish:
        break;
}

gotoとswitchを一緒に使用する:

switch(m_state) {
    case State.Start:
        goto case State.Data2;
    case State.Data1:
        goto case State.Finish;
    case State.Data2:
        m_state = State.Data1;
        //call to a function outside the state machine
        //that could possibly change the state
        break;
    case State.Finish:
        break;
}

私はgotoステートメントを使用する最初のオプションを好みます。これは、より高速で冗長性が少ないためです。しかし、それが最良の選択肢かどうかはわかりません。パフォーマンスに関しては多分ですが、読みやすさに関してはわかりません。だから私はこの質問をします。どのオプションが好きですか、そしてその理由は何ですか?

4

5 に答える 5

3

私は相互呼び出し/再帰関数を好みます。あなたの例を適応させるには:

returnvalue Start() {
    return Data();
}

returnvalue Data() {
    return Finish();
}

returnvalue Finish() {
    …
}

理論的には、これ完全にインライン化できるため、コンパイラの出力はソリューションと同等になりますgoto(したがって、同じ速度)。現実的には、C# コンパイラの /JITter はおそらくそれを行いません。しかし、ソリューションははるかに読みやすいので(まあ、IMHO)、速度の点で実際に劣っていること、またはスタックオーバーフローが発生することを証明する非常に慎重なベンチマークの後にのみ、ソリューションに置き換えます(この単純なソリューションではなくgoto、より大きなオートマトンはこの問題に遭遇します)。

それでも、私は間違いなく解決策に固執しgoto caseます. なんで?そうすれば、乱雑なgotoパスタ全体がブロック構造 (switchブロック) 内にうまく収まり、スパゲッティが残りのコードを台無しにすることがなくなり、ボロネーゼを防ぐことができます。

結論として、機能的なバリアントは明確ですが、一般的に問題が発生しやすいです。goto解決策は面倒です。goto case中途半端なクリーンで効率的なソリューションのみを提供します。パフォーマンスが本当に重要な場合 (そしてオートマトンがボトルネックである場合) は、構造化goto caseバリアントを選択してください。

于 2009-10-24T12:51:38.127 に答える
2

goto に対するスイッチの利点は、命令ポインターだけでなく、変数に状態があることです。

goto メソッドでは、ステート マシンが他のすべてを制御するメイン ループである必要があります。これは、ステートが失われるため、ステート マシンから抜け出すことができないためです。

switch メソッドを使用すると、ステート マシンが分離され、外部からのイベントを処理したい場所に移動できます。ステート マシンに戻ると、yuu が中断したところから続行します。複数のステート マシンを並べて実行することもできますが、これは goto バージョンでは不可能です。

3 番目の選択肢でどこに向かっているのかわかりません。最初の選択肢のように見えますが、役に立たないスイッチがあります。

于 2009-10-24T12:50:58.350 に答える
2

ステート マシンの遷移ロジックを個別の関数に分割したい場合は、switch ステートメントを使用する必要があります。

switch(m_state) {
        case State.Start:
                m_state = State.Data;
                break;
        case State.Data:                        
                m_state = ComputeNextState();
                break;
        case State.Finish:
                break;
} 

また、より読みやすく、switch ステートメントのオーバーヘッド (Goto に対して) は、まれな状況でのみパフォーマンスの違いをもたらします。

編集:

「goto case」を使用して、パフォーマンスを少し改善できます。

switch(m_state) {
        case State.Start:
                m_state = State.Data; // Don't forget this line!
                goto case State.Data;
        case State.Data:                        
                m_state = ComputeNextState();
                break;
        case State.Finish:
                break;
} 

ただし、状態変数の更新を忘れるリスクがあります。これは後で微妙なバグを引き起こす可能性があるため (「m_state」が設定されていると想定したため)、回避することをお勧めします。

于 2009-10-24T13:06:18.570 に答える
2

4番目のオプションがあります。

イテレータを使用してステートマシンを実装します。ここにその方法を示す素敵な短い記事があります

ただし、いくつかの欠点があります。イテレータの外側から状態を操作することはできません。

それが非常に速いかどうかもわかりません。しかし、いつでもテストを行うことができます。

于 2009-10-24T13:12:41.677 に答える
0

個人的には、最初のものは新しい状態に移行するために不要なループステップ(たとえば)を必要とするため、gotoを使用した2番目のものを好みます

于 2009-10-24T12:37:49.547 に答える