6

goto ですべてを作成するのは簡単ですが (f.ex. IL で証明されているように)、 Java でサポートされているすべてのものを使用して、高レベルの式とステートメントを含むすべてのgoto ステートメントを削除することも可能かどうか疑問に思っていました。

または、必要に応じて: 私が探しているのは、goto の作成方法に関係なく、常に機能する「書き換えルール」です。

それは主に理論的な質問として、純粋に興味として意図されています。良い/悪い習慣としてではありません。

私が考えた明らかな解決策は、次のようなものを使用することです。

while (true)
{
    switch (state) {
       case [label]: // here's where all your goto's will be
          state = [label];
          continue;
       default:
          // here's the rest of the program.
    }
}

これはおそらく機能し、私の「正式な」質問には合っていますが、私の解決策は少し好きではありません。1つには、それは非常に醜く、2つには、基本的にgotoを、gotoとまったく同じことを行うスイッチにラップします。

それで、より良い解決策はありますか?


更新 1

多くの人が質問が「広すぎる」と考えているように見えるので、もう少し詳しく説明します... 私が Java に言及した理由は、Java には「goto」ステートメントがないためです。私の趣味のプロジェクトの 1 つとして、私は C# コードを Java に変換しようとしていましたが、これは非常に困難であることがわかっています (Java のこの制限が原因の 1 つです)。

それは私に考えさせました。あなたがf.exを持っている場合 Open addressing での「remove」メソッドの実装 ( http://en.wikipedia.org/wiki/Open_addressing - 注 1 を参照)。例外的な場合に「goto」があると非常に便利ですが、この特定のケースでは「状態」変数を導入することで書き直すことができます。これは 1 つの例にすぎないことに注意してください。継続用のコード ジェネレーターを実装しました。これらのコード ジェネレーターは、逆コンパイルしようとすると大量の goto を生成します。

また、この問題の書き直しが常に「goto」ステートメントを排除するかどうか、およびすべての場合に許可されるかどうかもわかりません。私は正式な「証拠」を探しているわけではありませんが、この問題で排除が可能であるといういくつかの証拠は素晴らしいでしょう.

したがって、「広さ」については、「答えが多すぎる」または「goto を書き直す方法がたくさんある」と考えるすべての人々に、アルゴリズムまたは一般的なケースを書き直すためのアプローチを提供してください。これまでは私が投稿したものです。

4

5 に答える 5

12

この 1994 年の論文:コントロール フローの調整: Goto ステートメントを削除するための構造化されたアプローチでは、C プログラム内のすべての goto ステートメントを削除するアルゴリズムが提案されています。このメソッドは、C# または if/switch/loop/break/continue などの一般的な構造を使用する言語で記述されたすべてのプログラムに適用できます (私の知る限り、そうしない理由はわかりません)。

それは、2 つの最も単純な変換から始まります。

  • ケース1

    Stuff1();
    if (cond) goto Label;
    Stuff2();
    
    Label:
    Stuff3();
    

    になります:

    Stuff1();
    if (!cond)
    {
      Stuff2();
    }
    Stuff3();
    
  • ケース 2

    Stuff1();
    Label:
    Stuff2();
    Stuff3();
    if (cond) goto Label;
    

    になります:

    Stuff1();
    do
    {
      Stuff2();
      Stuff3();
    } while (cond);
    

その上に構築して、各複雑なケースを調べ、それらの些細なケースにつながる反復変換を適用します。次に、究極の goto/labels 根絶アルゴリズムで締めくくります。

これは非常に興味深い読み物です。

更新:この件に関する他の興味深い論文 (手に入れるのは簡単ではないので、参照用にここに直接リンクをコピーします):

Goto ステートメントを削除するための正式な根拠

McCat C コンパイラの Goto 消去法とその実装

于 2014-09-17T08:37:20.130 に答える
1

あなたはそれが理論的な質問だと言ったので、ここに理論的な答えがあります。

Javaはチューリング完全なので、もちろんそうです。Java で任意の C# プログラムを表現できます。Minecraft の Redstone や Minesweeper で表現することもできます。これらの代替手段と比較して、Java で表現するのは簡単なはずです。

ただし、明らかにより実用的な答え、つまり変換を行うためのわかりやすいアルゴリズムは、パトリスによって与えられています。

于 2014-09-17T08:46:00.150 に答える
1

私は自分の解決策が少し好きではありません。1つには、それは非常に醜く、2つには、基本的にgotoを、gotoとまったく同じことを行うスイッチにラップします。

これは、goto の使用を置き換えることができる一般的なパターン (goto とまったく同じことを行うもの) を探すときに、常に得られるものです。他に何がありますか?goto のさまざまな使用法は、最適な言語構造に置き換える必要があります。そのため、switch ステートメントや for ループとしての構造が最初に存在し、そのようなプログラム フローを簡単に作成し、エラーを起こしにくくします。コンパイラは引き続き goto (またはジャンプ) を生成しますが、失敗する箇所でも一貫して生成します。その上、コンパイラが生成するものを読み取る必要はありませんが、理解しやすいものを読み取る (および書き込む) ことができます。

ほとんどのコンパイラ構造は、goto の特定の使用法を一般化するために存在することがわかります。これらの構造は、その前に存在した goto 使用の一般的なパターンに基づいて作成されます。論文 Patrice Gahide は、その逆のプロセスを示していると述べています。コードに goto が見つかった場合は、それらのパターンのいずれかを調べているかのどちらかであり、その場合は、一致する言語構造に置き換える必要があります。または、本質的に構造化されていないコードを見ている場合は、実際にコードを構造化する (またはそのままにしておく) 必要があります。それを構造化されていないが構造化されていないものに変更すると、goto事態が悪化するだけです。(ちなみに、同じ一般化プロセスがまだ進行中です。foreach非常に一般的な使用法を一般化するためにコンパイラにどのように追加されているかを検討してくださいfor...)

于 2014-09-17T10:13:33.637 に答える