gotoを使用する価値はありますか?


29

gotoほとんどの場合、推奨されていません。このステートメントを使用する価値はありますか?



1
gotoCPUが最終的に行うことですが、ターゲットの終わりにマークがないため、人間が理解して抽象化する必要があるものとはうまく機能しません。Intercalと比較してくださいCOMEFROM

2
@ThorbjørnRavnAndersen、人間が状態機械の状態遷移について話しているとき、他に何を意味するのでしょうか?類似性が見えませんか?「特定の状態に進む」、それはそれが意味することであり、他のものはそのような些細なことの不必要な複雑さ以外の何物でもないでしょう。「マークなし」とはどういう意味ですか?ラベルはそのようなマークです。
SKロジック

@ SK-logicは、gotoを許可する距離に依存します。ステートマシンの実装にgotoは必要ありません。

@ThorbjørnRavnAndersenは、もちろんgoto必須ではありません。これは、状態遷移のセマンティクスに最も近いものなので、命令型言語でそれらを実装する最も合理的な方法です。そして、その宛先はソースからかなり離れている可能性があり、読みやすさを妨げることはありません。そのようなコードの私のお気に入りの例は、アドベンチャーゲームのDE Knuthの実装です。
SKロジック

回答:


49

これはStack Overflowで何度か議論されてきましたが、Chris Gillumは次の可能な用途を要約しましたgoto

関数をきれいに終了する

多くの場合、関数内でリソースを割り当て、複数の場所で終了する必要があります。プログラマは、関数のすべての「終了ポイント」がすべてクリーンアップラベルに移動するように、関数の最後にリソースクリーンアップコードを配置することにより、コードを簡素化できます。このように、関数の「終了ポイント」ごとにクリーンアップコードを記述する必要はありません。

ネストされたループを終了する

ネストされたループ内にあり、すべてのループから抜け出す必要がある場合、gotoを使用すると、breakステートメントやif-checkよりもはるかにクリーンでシンプルになります。

低レベルのパフォーマンスの改善

これはパフォーマンスが重要なコードでのみ有効ですが、gotoステートメントは非常に高速に実行され、関数内を移動するときに後押しすることができます。ただし、コンパイラは通常、gotoを含むコードを最適化できないため、これは両刃の剣です。

私は、他の多くの人が主張するように、これらのすべてのケースで、使用はgotoコード化されたコーナーから抜け出す手段として使用され、一般的にリファクタリング可能なコードの症状であると主張します。


28
最初の問題はfinally現代言語のブロックによって非常にきれいに解決され、2番目の問題はラベル付きbreakのs によって解決されます。ただし、Cにこだわっている場合は、gotoこれらの問題をエレガントに解決する唯一の方法です。
チンメイカンチ

2
非常に良い点。Javaが複数レベルのループから抜け出すことができる方法が好きです
-Casebash

4
@Chinmay-最終的にブロックが適用されるのは、1)それらを持つ最新の言語、およびb)オーバーヘッドに耐えることができる(例外処理にはオーバーヘッドがあります)。つまり、使用finallyはそれらの条件下でのみ有効です。gotoを使用することが非常にまれですが、有効な状況があります。
luis.espinal

21
オーケー人...一体何が違いですbreak 3break myLabelgoto myLabel。ああ、まさにキーワードです。を使用している場合breakcontinueまたは類似のキーワードを使用しているgoto場合(使用しfor, while, foreach, do/loopている場合は条件付きgotoを使用している場合)
マシュー・ホワイト

4
低レベルのパフォーマンスについて-最新のプロセッサの動作を予測するのは難しいかもしれません(パイプライン、アウトオブオーダー実行)。@MatthewWhited:スコーピング。任意のポイントでスコープに入った場合、どのコンストラクターを呼び出すべきかは(人間にとって)明確ではありません(Cにも問題が存在します)。
マチェイピエチョトカ

42

上位レベルの制御フロー構造は、問題領域の概念に対応する傾向があります。if / elseは、何らかの条件に基づいた決定です。ループは、何らかのアクションを繰り返し実行することを意味します。breakステートメントでも「これを繰り返し行っていましたが、今は停止する必要があります」と書かれています。

一方、gotoステートメントは、問題の領域ではなく、実行中のプログラムの概念に対応する傾向があります。それはプログラムの指定されたポイント実行を続けると言います。コードを読んでいる人は、問題のドメインに関してそれが何を意味するかを推測する必要があります。

もちろん、すべての高レベルの構造は、gotoおよび単純な条件分岐の観点から定義できます。だからといって、彼らが単に変装したゴトだというわけではありません。それらを制限されたゴトと考えてください-そしてそれはそれらを有用にする制限です。breakステートメントは、ループを囲むループの最後へのジャンプとして実装されますが、ループ全体を操作していると考える方が適切です。

他のすべてが等しい場合、問題のドメインの構造を反映した構造のコードは、読みやすく保守しやすい傾向があります。

gotoステートメントが絶対に必要な場合はありません(その効果の定理があります)が、それが最も悪い解決策になる場合があります。これらのケースは、言語がサポートする上位レベルの構成に応じて、言語ごとに異なります。

たとえば、Cでは、gotoが適切な3つの基本的なシナリオがあると思います。

  1. ネストされたループから抜け出します。言語にラベル付きbreakステートメントがある場合、これは不要です。
  2. エラーまたはその他の予期しないイベントが発生した場合に、一連のコード(通常は関数本体)から解放します。言語に例外がある場合、これは不要です。
  3. 明示的な有限状態マシンの実装。この場合(そして、この場合のみ)、gotoは問題ドメインの概念に直接対応し、ある状態から指定された別の状態に移行します。現在の状態は、現在実行中のコードブロックによって表されます。 。

一方、明示的な有限状態マシンは、ループ内でswitchステートメントを使用して実装することもできます。これには、すべての状態がコード内の同じ場所から開始されるという利点があります。これは、デバッグなどに役立ちます。

かなり現代的な言語(if / elseおよびループをサポートする言語)でgotoを使用する主な目的は、言語にない制御フロー構成をシミュレートすることです。


5
これは実際のところ、これまでのベストアンサーです。1年半前の質問に対しては、かなり驚くべきことです。:-)
コンラッドルドルフ

1
「これまでのベストアンサー」に+1。---「適度に現代的な言語(if / elseおよびループをサポートする言語)でgotoを使用する主な目的は、言語にない制御フロー構成をシミュレートすることです。」
デイブドップソン

switch-statementステートマシンでは、制御値へのすべての書き込みは実際にはa gotoです。SSSMは、実行状態からSM状態を分離するので、使用するのに適した構造であることがよくありますが、実際には "gotos"を排除しません。
supercat

2
「他のすべてが等しい場合、問題のドメインの構造を反映した構造のコードは、読みやすく保守しやすい傾向があります。」 私はこれを長い間完全に知っていましたが、他のプログラマーが実際に理解できる方法で正確に伝えるための言葉が欠けていました。これありがとう。
ワイルドカード

「構造化プログラム定理」は、構造化プログラミングステートメントを使用してgotoをエミュレートすることがgotoを使用するよりもはるかに読みにくいという点を明確に示す傾向があるため、私を楽しませます。

11

確かにプログラミング言語に依存します。主な理由gotoは議論の余地がありますが、それはコンパイラがそれをあまりにも自由に使用できるようにするときに生じる悪影響のためです。たとえば、goto初期化されていない変数にアクセスしたり、さらに悪いことに別のメソッドにジャンプして呼び出しスタックを台無しにできるような方法で使用できる場合、問題が発生する可能性があります。無意味な制御フローを許可しないのは、コンパイラの責任です。

Javaはgoto完全に拒否することでこの問題を「解決」しようとしました。ただし、Javaではブロックreturn内で使用finallyできるため、例外が誤って飲み込まれます。同じ問題がまだあります:コンパイラは仕事をしていません。goto言語から削除しても、修正されていません。

C#では、goto同様に安全ようでbreakcontinuetry/catch/finallyreturn。初期化されていない変数を使用したり、finallyブロックなどから飛び出させたりすることはできません。コンパイラは文句を言います。これは、私が無意味な制御フローを言ったように、実際の問題を解決するからです。goto明確な割り当ての分析やその他の妥当なコンパイラチェックを魔法のようにキャンセルしません。


あなたのポイントは、C#gotoは悪ではないということですか?もしそうなら、それはgotoC#だけでなく、コンストラクトで日常的に使用されるすべての現代言語の場合です
...-lvella

9

はい。あなたのループが深い複数のレベルにネストされているときに、gotoあるだけエレガントに内部ループを抜け出しの方法。もう1つのオプションは、フラグを設定し、そのフラグが条件を満たす場合に各ループから抜け出すことです。これは本当に見苦しく、かなりエラーが発生しやすいです。これらの場合、goto単に優れています。

もちろん、Javaのラベル付きbreakステートメントは同じことをしますが、コード内の任意のポイントにジャンプすることを許可せずに、goto悪をもたらすものを許可することなく問題をきれいに解決します。


誰が下票を説明したいですか?これは...質問には完全に有効な答えだった
Chinmay Kanchi

4
gotoは決してエレガントな答えではありません。ループがいくつかのレベルの深さにネストされている場合、問題は、多重ネストされたループを回避するためにコードを設計できなかったことです。手続き呼び出しは敵ではありません。(「Lambda:The Ultimate ...」の論文をお読みください。)gotoを使用して複数レベルの深さでネストされたループから抜け出すことは、開いている複数の骨折にバンドエイドをかけることです。回答。
ジョンR.ストローム

6
そしてもちろん、深くネストされたループが必要になるという奇妙なケースはありませんか?優れたプログラマーになるということは、ルールに従うことだけではなく、ルールを破るタイミングを知ることでもあります。
Chinmay Kanchi

2
@ JohnR.Strohm決して言わないでください。プログラミングで "never"のような用語を使用する場合、コーナーケース、最適化、リソース制約などを明らかに扱っていない。深くネストされたループでは、gotoはループを終了する最もクリーンでエレガントな方法です。コードをどれだけリファクタリングしたかは関係ありません。
スジェイファドケ16

@ JohnR.Strohm:VB.NETからC#への切り替えで2番目に見逃された機能:Doループ。どうして?Doループとタイプへの深いブレークが必要な1つのループを変更できるためExit Do、ありませんGoTo。ネストされたループは常に発生します。スタックの破壊は、いくつかの時間に発生します。
ジョシュア

7

落胆の大部分は、1960年代前半に無差別の力について説得力を持っていたジクストラ神の周りに作成された一種の「宗教」から生じます。

  • 任意のコードブロックに任意の場所にジャンプする
    • 最初から実行されていない関数
    • 最初から実行されないループ
    • 変数の初期化をスキップしました
  • クリーンアップせずに、コードのブロックから飛び出します。

これはgoto現代言語の声明とは何の関係もありません。現代言語の存在は、言語が提供するもの以外のコード構造の作成をサポートしているためです。

特に、上記の最初の主要なポイントは許可され、2番目のポイントgotoは削除されます(ブロックから出た場合、スタックは適切に巻き戻され、すべての適切なデストラクタが呼び出されます)

この回答を参照して、gotoを使用していないコードでも読み込めない可能性があることを理解してください。問題はgoto自体ではなく、不適切な使用です。

私は使用せずに、プログラム全体を書くことができるifだけで、for。もちろん、読みにくく、不器用で不必要に複雑に見えます。

しかし、問題はそうではありませんfor。それは私です。

以下のようなものbreakcontinuethrowbool needed=true; while(needed) {...}、などは、以上に注目されている仮面舞踏会 goto、現代laguages-の発明後-50年はまだ彼らの囚人を望んでいることを、Djikstrarian狂信者のシミターから離れて脱出します。彼らは、ジクストラが話していたことを忘れ、彼のメモのタイトルのみを覚えています(GOTOは有害であると考えられ、彼のタイトルでもありません:編集者によって変更されました)。順番に配置された文字。

2011年です。ジクストラが説得力のgotoあるGOTO声明に対処することに注目していることを理解する時が来ました。


1
「break、continue、throw、bool needed = true、while(needed){...}などは、gotoの仮面舞踏会以上のものに注目している」と私は同意しません。「休憩などは制限された goto」、またはそのようなものを好むでしょう。gotoの主な問題は、通常は強力すぎることです。現在のgotoがダイクストラのgotoよりも強力でない限り、あなたの答えは私と一緒です。
ムハンマドアルカウリ

2
@MuhammadAlkarouri:まったく同感です。あなたはちょうど私のコンセプトを正確に表現するためのより良い表現を見つけました。私のポイントは、これらの制限が適用できない場合があり、必要な制限が言語にないことです。次に、専門性の低い、より「強力な」ものが回避策を可能にします。たとえば、Java break nはC ++では使用できないため、n-pleループから抜け出す必要がないgoto escape場合でもですescape
エミリオガラヴァリア

4

あちこちの奇妙なgotoは、それが関数に対してローカルである限り、読みやすさを大きく損なうことはめったにありません。多くの場合、このコードでは、通常とは異なる制御構造の使用を必要とする異常な処理が行われているという事実に注意を向けることで、メリットがあります。

(ローカル)gotoが読みやすさを著しく損なっている場合、通常はgotoを含む関数が複雑になりすぎていることを示しています。

最後にCコードに入れたのは、インターロックループのペアを作成することでした。「許容できる」goto使用の通常の定義には適合しませんが、結果として、関数は大幅に小さくなり、明確になりました。gotoを回避するには、特にDRYの乱雑な違反が必要でした。


1
これに対する答えは、古い「レイズポテトチップス」のスローガンで最も気前よく述べられています。「1つだけは食べられません」。あなたの例では、2つの「連動」(それが意味するものは何でも、私はそれはきれいではないと賭けています)ループを持っています、そしてgotoはあなたに安いアウトを与えました。あなたがバスにぶつかった後、誰がそのコードを変更しなければならない保守担当者はどうですか?後藤は彼の人生を楽にするのか、それとも難しくするのか?これらの2つのループを再考する必要がありますか?
ジョンR.ストローム

3

私はこの問題全体が間違ったツリーをほおばっているケースだと思う。

GOTO自体は問題に思えませんが、むしろ実際の罪の症状であるスパゲッティコードです。

GOTOがフロー制御ラインの大きな交差を引き起こす場合、それは悪い期間です。フロー制御ラインを通過しない場合、無害です。中間の灰色のゾーンにはループの救済などがありますが、すべての正当な灰色のケースをカバーする構造を追加していない言語がまだあります。

私が長年実際に使用しているのは、決定点がループの中間にあるループの場合だけです。重複したコード、フラグ、またはGOTOが残っています。GOTOソリューションは3つの中で最高だと思います。ここにはフロー制御線が交差することはなく、無害です。


あなたがほのめかすケースは時々「loop and half」または「N plus one half loop」と呼ばgotoれ、ループにジャンプするsの有名なユースケースです(言語がこれを許可している場合;他のケースでは、ジャンプする中央のループから出て、通常はgoto)なしで簡単です。
コンラッドルドルフ

(残念ながら、ほとんどの言語はループにジャンプすることを禁止しています。)
コンラッドルドルフ

@KonradRudolph:GOTOを使用して「ループ」を実装する場合、ジャンプする他のループ構造はありません。
ローレンペクテル

1
ここで制御フローgotoを叫ぶことは、通常の構造化プログラミングパターンに適合しないと考えています。構造化されたプログラミングパターンに適合する制御フローは、適合しないものよりも推論が容易であるため、可能であればそのようなパターンの使用を試みる必要があります。コードがそのようなパターンに適合している場合、適合しないことを叫ぶべきではありません。一方、コードが実際にそのようなパターンに適合しない場合は、コードが実際に適合しないふりをするよりも、それについて叫ぶほうが良いかもしれません。
supercat

1

はい、gotoを使用して開発者のエクスペリエンスを向上させることができます。http//adamjonrichardson.com/2012/02/06/long-live-the-goto-statement/

ただし、強力なツール(ポインター、多重継承など)と同様に、それを使用して訓練する必要があります。リンクで提供される例では、goto構造の使用を同じ関数/メソッドに制限し、新しい制御ブロック(ループ、switchステートメントなど)にジャンプする機能を無効にするPHPを使用しています。


1

言語に依存します。たとえば、Cobolプログラミングでまだ広く使用されています。また、ファームウェアプログラミング言語が初期のBASIC方言であるため、当然Gotoを使用する必要があるBarionet 50デバイスで作業しました。


0

私はノーと言うでしょう。GOTOを使用する必要がある場合は、コードを再設計する必要があると思います。


3
たぶん、しかしあなたは何の理由も提供していない
-Casebash

ダイクストラは、数十年前に詳細に推論を提供しました。
ジョンR.ストローム

2
ただのドグマ。応答なし。注:Djikstraはもはや有効ではありません。60年代初期のBASICまたはFORTRAN GOTOステートメントを参照していました。彼らは今日の本当に貧弱な(それらの力と比較して)ものとは何の関係もありません。
エミリオガラヴァリア

0

goto従来のアセンブラコードをCに移植する場合に役立ちます。最初の例ではgoto、アセンブラのbranch命令の代わりとしてCに命令ごとに変換すると、非常に高速な移植が可能になります。


-1

私は主張しません。gotoが解決に使用している問題に固有のツールに常に置き換える必要があります(言語で使用できない場合を除く)。たとえば、breakステートメントと例外は、ループエスケープとエラー処理の以前に解決された問題を解決します。


言語にこれらの両方の機能gotoがある場合、それらの言語には通常存在しないと私は主張します。
チンメイカンチ

しかし、それは彼らがそれらを必要としないからですか、それとも人々は彼らがそれらを必要としないと思うからですか?
マイケルK

あなたの意見は偏見です。goto非常に問題のあるドメインセマンティクスに最も近いのは、多くの場合です。それ以外のものはダーティハックになります。
SKロジック

@ SK-logic:「bigoted」という言葉は、あなたがそれが意味すると思うものを意味するとは思わない。
キーストンプソン

1
@Keith、「彼または彼女自身の意見と偏見に無礼に専念します」-それは私がここで一貫して観察していることです。「常に置き換えられるべき」は、少なくとも狂信者の言葉です。適切で健全な意見に近いものはなく、単なる純粋な偏見です。この「常に」は、他の意見のための余地を残していません。
SKロジック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.