後藤はまだ有害だと考えていますか?[閉まっている]


282

誰もがダイクストラの編集者へ手紙を知っています。有害と見なされるステートメントに移動しここでも .htmlトランスクリプトとここに .pdf)、その時点からgotoステートメントを可能な限り避けようとする手ごわいプッシュがありました。gotoを使用して、保守不可能な大規模なコードを生成することは可能ですが、それでも最新のプログラミング言語には残っています。Schemeの高度な継続制御構造でさえ、洗練されたgotoとして説明できます。

gotoを使用する必要があるのはどのような場合ですか?いつ避けるのが最善ですか?

追加質問として:Cは、現在のスタックフレーム内だけでなく、任意の呼び出しフレーム内に移動する機能を提供する、setjmpとlongjmpの2つの関数を提供します。これらはgotoと同じくらい危険だと考えるべきですか?もっと危険な?


ダイクストラ自身はその肩書きを後悔したが、彼には責任がなかった。EWD1308ここでも .pdf)の終わりに彼は書きました:

最後に、記録のための短編小説。1968年、ACMの通信は「有害と見なされたgotoステートメント」というタイトルで私のテキストを公開しましたが、これは後年最も頻繁に参照されることになりますが、残念ながら、それよりも多くの場合、テンプレートになることで私の名声の土台となったタイトル:「Dijkstra有害と見なされる」というタイトルのタイトルを含むほとんどすべてのXについて、「X有害と見なされる」というタイトルの下にあらゆる種類の記事が表示されます。しかし、何が起こったのでしょうか?「後藤声明に反対する訴訟」というタイトルで論文を提出しました「それは、その出版をスピードアップするために、編集者は「編集者への手紙」に変わりました、そしてその過程で彼は彼自身の発明の新しいタイトルを与えました!編集者はニクラウス・ワースでした。

このトピックについてよく考え抜かれた古典的な論文は、ダイクストラの論文と一致するように、Donald E. Knuthによるgo to Statementsを使用した構造化プログラミングです。両方を読むことは、文脈の再確立と主題の非独断的な理解に役立ちます。この論文では、このケースに関するダイクストラの意見が報告されており、さらに強力です。

ドナルドE.クヌース:そのような見解を示すことで、ダイクストラの考えにはっきりと反対しているのではないと私は信じています。彼が最近次のように書いたからです。ステートメントに行く]。プログラミングの概念的な問題は、単純な形式のコーディング規律によって1つのトリックで解決できるかのように、他の人がそれを信仰しているという不快な気持ちを持っています! "


1
C#のgotoは、ダイクストラが話していた理由から、ダイクストラが話していたgotoと同じではありません。その gotoは、当時と同じように有害ですが、現代の言語では代替の制御構造が提供されているため、必要性ははるかに低くなります。C#gotoは非常に制約されています。
2008

25
後藤は、明快さを追加する場合に適しています。ネストされたループが長い場合は、「ブレーク」変数を設定して、抜けるまでブレークするよりも、ループから抜けた方がよい場合があります。
simendsjo 2010年

28
4つの深度にネストされたループがある場合(それが良いことではありません)、すべてから抜け出すには、一時的な値を設定する必要があります。ここでのgotoの方がはるかにわかりやすく、IDEはgotoの場所を簡単に示すことができます。そうは言っても、gotoの使用はまばらである必要があり、私の意見では、コードをスキップするために下に移動するだけです
simendsjo

7
タグ付けされた9000スレッドと1スレッドを読むことをお勧めしますgoto
Matti Virkkunen、2010

5
使用するより明らかに悪い点が1つあります。goto構造化プログラミングツールをハッキングして、を実装することgotoです。

回答:


184

次のステートメントは一般化です。例外を訴えることは常に可能ですが、通常(私の経験と控えめな意見では)リスクを冒す価値はありません。

  1. メモリアドレス(GOTOまたはrawポインターのいずれか)を無制限に使用すると、簡単に回避できる間違いを犯す機会が多すぎます。
  2. コード内の特定の「場所」に到達する方法が多いほど、その時点でのシステムの状態を確信できなくなります。(下記参照。)
  3. 構造化プログラミングIMHOは、「GOTOを回避する」ことよりも、コードの構造をデータの構造に一致させることを重視しています。たとえば、繰り返しデータ構造(配列、シーケンシャルファイルなど)は、コードの繰り返し単位によって自然に処理されます。組み込みの構造(たとえば、while、for、until、for-eachなど)があると、プログラマーは同じ決まりきったコードパターンを繰り返す面倒な作業を回避できます。
  4. GOTOが低レベルの実装の詳細であっても(常にそうとは限りません!)、プログラマが考えるべきレベルより下です。何人のプログラマーが生のバイナリで個人小切手帳のバランスをとっていますか?データベースエンジンへのキーを提供するだけでなく、ディスク上のどのセクターに特定のレコードが含まれるかを心配するプログラマーは何人いますか?

上記の脚注:

ポイント2については、次のコードを検討してください。

a = b + 1
/* do something with a */

コードの「何かをする」時点で、aより大きい信頼度で述べることができbます。(はい、トラップされていない整数のオーバーフローの可能性は無視しています。簡単な例を押しつぶさないでください。)

一方、コードが次のように読み取られた場合:

...
goto 10
...
a = b + 1
10: /* do something with a */
...
goto 10
...

方法の多様性は、私たちが仕事にはるかに困難との関係についての自信を持ってする必要があることを10の手段にラベルを付けるために取得するaと、bその時点で。(実際、一般的なケースでは決定不可能です!)

ポイント4に関しては、コード内の「どこかに行く」という概念全体は単なる比喩です。電子とフォトン(廃熱用)を除いて、CPU内のどこにも実際には「行きません」。時には、私たちは別の、より有用なもののための隠喩をあきらめます。(数十年前に!)

if (some condition) {
  action-1
} else {
  action-2
}

は、アクション1とアクション2をアウトオブラインパラメータレスルーチンとしてコンパイルし、条件のブール値を使用してどちらか一方を呼び出す単一の2引数VMオペコードを使用して、仮想マシンに実装されました。コンセプトは、「ここに行くかそこに行くか」ではなく、単に「今何を呼び出すかを選択する」でした。繰り返しになりますが、単なる比喩の変更です。


2
良い点。高水準言語では、gotoは実際には何も意味しません(Javaのメソッド間でジャンプすることを検討してください)。Haskell関数は単一の式で構成されます。gotoでジャンプしてみてください。
メカニカルカタツムリ

2
Postscriptは、ポイント4の例のように機能します。
luser droog 2012年1

Smalltalkは、ポイント4の例と同様に機能しますが、「同様に」とは、「手続き型言語のように何もしない」という意味です。:P言語にはフロー制御はありません。すべての決定は、ポリモーフィズムを介して処理(されているtruefalse、異なるタイプのもの)、および場合/他の各ブランチは、基本的にはラムダです。
cHao 14

これらは有効なポイントですが、最終的には、「誤用された場合」にgotoがいかに悪いかを繰り返します。Break、Continue、Exit、Return、gosub、settimeout、global、includeなどはすべて、物事の流れを精神的に追跡する必要のある最新の手法であり、スパゲッティコードの作成やスキップして変数の状態の不確実性を作成するために誤用される可能性があります。公平を期すために、私はgotoの悪用を直接目にしたことはありませんが、1度または2度使用されることもありません。これは、常により良いものがあるという声明を物語っています。
Beejor

goto最新のプログラミング言語(Go)で。stackoverflow.com/ a/ 11065563/3309046
nishanths 2016年

245

XKCDのGOTOコミック

私の同僚は、GOTOを使用する唯一の理由は、あなたが自分自身を隅々までプログラムして、それが唯一の方法である場合だと言いました。つまり、事前に適切な設計を行っておけば、後でGOTOを使用する必要がなくなります。

この漫画は、「プログラムのフローを再構築したり、代わりに少し「GOTO」を使用したりできる」と美しく示していると思いました。GOTOは、デザインが弱い場合の弱点です。 ヴェロキラプトルは弱者を捕食する


41
GOTOは、任意のスポットから別のスポットに「ジャンプ」できます。ヴェロキラプトルはどこからともなくここに飛び込んだ!
rpattabi

29
あなたが実行する前にリンクする必要があることを誰もが知っているので、私は冗談をまったく面白いとは思わない
Kinjal Dixit 2010

31
あなたの同僚は間違っており、明らかにKnuthの論文を読んでいません。
ジムバルター、2013年

27
gotoを回避するためだけに、何年にもわたって難読化された、または他の方法で歪められたコードの終わりは見られません。@jimmcKeeth、上のxkcdの漫画はgotoが弱いことを証明していません。それはそれを使用する周りのヒステリーをからかっている。

4
Delphiライブラリのソースコードにgotoステートメントが含まれていることにお気づきでしょう。そして、これらはgotoの適切な使用法です。
David Heffernan 2015年

131

単一の関数内での例外処理の代わりにGOTOを使用することが有効な場合があります。

if (f() == false) goto err_cleanup;
if (g() == false) goto err_cleanup;
if (h() == false) goto err_cleanup;

return;

err_cleanup:
...

COMコードはかなり頻繁にこのパターンに該当するようです。


29
gotoがコードを簡素化し、読みやすく維持しやすい正当なユースケースがあることに同意します。しかし、ある種のgoto-恐怖症が浮かんでいるようです...
Pop Catalin

48
@ボブ:ローカル変数をクリーンアップする場合、err_cleanupコードをサブルーチンに移動するのは困難です。
ニキ

4
実は、私は私がいなかったという理由だけでCOM / VB6でそれを使用して何のことは、代替なかったので、代わりに。今日、try / catch /ついに私は今日どれだけ幸せだ。
Rui Craveiro、

10
@ user4891慣用的なC ++の方法ではありません{} catch(){cleanup; }ではなく、RAII。クリーンアップが必要なリソースはデストラクタでクリーンアップされます。すべてのコンストラクタ/デストラクタは、1つのリソースを管理します。
David Stone

5
gotoなしでこれをCで書く方法は2つあります。そしてどちらもはるかに短いです。if(f())if(g())if(h())は成功を返します。掃除(); 戻り失敗; または:if(f()&& g()&& h())成功を返します。掃除(); 戻り失敗;
LHP、2014年

124

gotoの使用は1回しか思い出せません。一連の5つのネストされたカウントループがあり、特定の条件に基づいて、構造全体を内側から早く抜け出せるようにする必要がありました。

for{
  for{
    for{
      for{
        for{
          if(stuff){
            GOTO ENDOFLOOPS;
          }
        }
      }
    }
  }
}

ENDOFLOOPS:

ブールブレーク変数を簡単に宣言して、各ループの条件の一部として使用することもできましたが、この例では、GOTOが実用的で読みやすいと判断しました。

ヴェロキラプターが私を攻撃しなかった。


83
「それを関数にリファクタリングし、gotoをreturn :)に置き換えます」と違いは何ですか?本当に違いは何ですか?帰宅もしませんか?リターンもgotoのような構造化されたフローをブレーキします。この場合、gotoが同じように動作します(たとえgotoが悪意のあるものに使用できる場合でも)
Pop Catalin

38
多くのループを入れ子にすることは通常、コードがそれ自体のすべての匂いを嗅ぐことです。5次元配列の乗算などを行わない限り、内部ループの一部を小さな関数に効果的に抽出できない状況を想像するのは困難です。すべての経験則と同様に、私が思うにいくつかの例外があります。
Doug McClean

5
リターンで置き換えることは、リターンをサポートする言語を使用している場合にのみ機能します。
Loren Pechtel、2009年

31
@leppie:反抗しgotoて構造化プログラミングを提供してくれた世代も、同じ理由で初期のリターンを拒否しました。それは、コードがどれほど読みやすいか、プログラマーの意図をどれほど明確に表現しているかにかかっています。悪意のあるキーワードの使用を避ける以外の目的で関数を作成すると、まとまりが悪くなります。治癒は病気よりも悪いです。
泥沼、

21
@ButtleButkus:率直に言って、それは悪くはないにしても、それと同じくらい悪いことです。少なくともを使用すると、ターゲットを明示的に指定gotoできます。を使用すると、(1)宛先を見つけるためにループの終了を数える必要があります。(2)ループ構造が変更された場合、宛先を正しく保つためにその番号を変更する必要がある場合があります。私が回避しようとしている場合、そのようなものを手動で追跡する必要がないことで利益が得られるはずです。break 5;goto
cHao 2013年

93

我々はすでに、この持っていた議論をし、私はで立って私のポイント

さらに、高レベルの言語構造を「goto変装している」と表現している人々にはうんざりしています。例えば:

Schemeの高度な継続制御構造でさえ、洗練されたgotoとして説明できます。

それはまったくナンセンスです。すべての制御構造を実装することができますgotoが、この観察はまったく簡単で役に立たないものです。gotoポジティブな効果のために有害とは見なされませんが、ネガティブな結果のため、これらは構造化プログラミングによって排除されました。

同様に、「GOTOはツールであり、他のすべてのツールと同様に、使用および悪用される可能性があります」という表現は完全に不適切です。現代の建設作業員は、岩を使用してそれを「道具である」と主張することはありません。岩はハンマーに置き換えられました。goto制御構造に置き換えられました。建設労働者がハンマーなしで荒野で立ち往生している場合、もちろん彼は代わりに岩を使用します。プログラマーが機能Xを備えていない劣ったプログラミング言語を使用しなければならない場合は、もちろん、goto代わりに彼女を使用する必要があります。しかし、彼女が適切な言語機能の代わりに他の場所でそれを使用する場合、彼女は明らかに言語を適切に理解しておらず、誤って使用しています。それは本当にそれと同じくらい簡単です。


82
もちろん、岩の適切な使用はハンマーとしてではありません。その適切な用途の1つは、砥石、または他の工具を研ぐためです。低い岩でも、適切に使用すると良いツールです。あなただけの適切な使用法を見つける必要があります。gotoも同様です。
Kibbee 2008年

10
では、後藤の適切な使用法は何ですか?考えられるすべてのケースで、より適した別のツールがあります。でも、あなたの砥石は、実際に彼らはまだ作られていても、今日ではハイテクツールによって置き換えられる岩。原材料と道具には大きな違いがあります。
Konrad Rudolph、

8
@jalf:後藤は最も確かにありません C#には存在しています。stackoverflow.com/questions/359436/…を
Jon Skeet

51
多くの人がこの投稿を承認するので、私はがっかりしています。あなたの投稿は効果的であるように見えましたが、実際に実行しているロジックに疑問を抱くことがなかったため、誤解に気づかなかったためです。投稿全体を言い換えさせてください。「あらゆる状況でgotoを実行するための優れたツールがあるため、後藤は決して使用しないでください。」これは論理的な二条件であり、そのため、投稿全体が本質的に「あらゆる状況でgotoに優れたツールがあることをどのようにして知ることができるのか」という疑問を抱いています。
スタイルでのコーディング

10
@コーディング:いいえ、投稿の要点を完全に逃しました。それはだった反撃ではなく隔離、完全な引数。私は主な論点「の」の誤りを指摘しただけgotoです。私はgotoそれ自体に反対する議論を提供しない限り、あなたは正しいです-私はそうするつもりはありませんでした-ですから質問をすることはありません。
Konrad Rudolph

91

後藤は、プログラムのためだけに含めるための私のリストの非常に低いです。それが受け入れられないという意味ではありません。

Gotoはステートマシンに最適です。ループ内のswitchステートメントは(一般的に重要な順に):(a)実際には制御フローを表していない、(b)見苦しい、(c)言語やコンパイラによっては非効率的である可能性があります。したがって、状態ごとに1つの関数を記述し、「return NEXT_STATE;」のようなことを行うことになります。gotoのようにも見えます。

確かに、ステートマシンを理解しやすい方法でコーディングすることは困難です。ただし、gotoを使用しても問題は発生せず、代替の制御構造を使用しても軽減できません。あなたの言語が「状態機械」構造を持たない限り。鉱山はそうではありません。

アルゴリズムが実際には、特定の制御フロー(ループ、条件、その他)ではなく、制限された一連の許容遷移(ゴト)によって接続されたノード(状態)のシーケンスを通るパスに関して最も理解しやすい場合)、その場合はコードで明示する必要があります。そして、あなたはきれいな図を描くべきです。

setjmp / longjmpは、例外または例外のような動作を実装するのに適しています。例外的に称賛されるわけではありませんが、例外は一般に「有効な」制御構造と見なされます。

setjmp / longjmpは、正しく使用することが難しいという意味でgotoよりも「危険」です。

悪いコードを書くことが最も難しい言語は、これまでになく、またないでしょう。-ドナルドクヌース。

Cからgotoを取り除いても、Cで優れたコードを作成するのは簡単ではありません。実際、Cが栄光のあるアセンブラー言語として機能することができるはずであるという点を逃してしまいます。

次に、「有害と見なされるポインタ」、「有害と見なされるダックタイピング」になります。それでは、安全でないプログラミング構造を奪うために彼らがあなたを守るために残されるのは誰ですか?え?


14
個人的に、これは私が小切手を与えたであろうコメントです。読者に指摘したいのは、難解な用語「ステートマシン」には、字句解析器などの日常的なものが含まれるということです。lex somtimeの出力を確認してください。後藤だらけ。
TED

2
ループ(またはイベントハンドラー)内でswitchステートメントを使用して、ステートマシンを正常に実行できます。jmpやgotoを使わなくても、たくさんのステートマシンを実行してきました。
スコットウィットロック

11
+1ステートマシン上のこれらの矢印は、他のどの制御構造よりも「goto」に近くマッピングされます。もちろん、ループ内でスイッチを使用することもできます-他の問題に対して、しばらくの間ではなく、一連のgotoを使用できるのと同じですが、それは通常アイデアです。これがこの議論の要点です。
エドマンド

2
最後の段落であなたを引用できますか?
Chris Lutz、

2
そして、ここ2013年には、「ポインターは有害と見なされる」フェーズに既に到達しています(そして、それを少し超えています)。
Isiah Meadows 14

68

Linux:カーネルコードで後藤を使用してカーネルトラップに、Linus Torvalds氏とLinuxのコード内の型GOTOの使用について「新しい男」との議論があります。非常に良い点がいくつかあり、Linusはいつもの傲慢さに身を包んだ:)

いくつかの通路:

Linus:「いいえ、あなたはCSの人々から洗脳されました。彼はNiklaus Wirthが実際に彼が話していることを知っていたと思っていました。

-

Linus:「gotoは問題ないと思いますが、大量のインデントよりも読みやすいことが多いです。」

-

Linus:「もちろん、Pascalのような愚かな言語では、ラベルが説明的ではないので、gotoは悪くなる可能性があります。」


9
それはどのように良い点ですか?彼らは他に何もない言語での使用について議論しています。アセンブリでプログラミングしている場合、すべての分岐とジャンプ gotoのジャンプです。そしてCは「移植可能なアセンブリ言語」であり、そうであった。また、通路はあなたが言うの引用ないについて、なぜ彼は後藤が良いと思うが。
2008

5
ワオ。それを読むのは残念です。Linus Torvaldsのような大きなOSの人は、それを言うよりもよく知っていると思います。Pascal(古いObject Pascalではなく、昔ながらのPascal)は、68k時代にMac OS Classicが書かれたものであり、当時の最も先進的なオペレーティングシステムでした。
メイソンウィーラー

6
@masonクラシックMac OSにはいくつかのPascalライブラリがありましたが(最終的には初期のMacではPascalランタイムがメモリを消費しすぎました)、コアコードの大部分はアセンブラー、特にグラフィックスとUIルーチンで作成されました。
ジムDovey

30
Linusは、終了ステータスを処理するためのgotoについて(その議論でのRik van Rielのように明示的に)主張するだけであり、Cの代替構成体が代わりに使用された場合にもたらされるであろう複雑さに基づいてそうします。
Charles Stewart、

21
私見Linusはこの問題について正しい。彼のポイントは、例外処理に似たものを実装する必要があるCで書かれたカーネルコードは、gotoを使用して最も明確かつ単純に書かれているということです。イディオムは、goto cleanup_and_exit我々が持っているということになりました左のgotoのいくつかの「良い」用途の一つであるforwhileif私たちの制御フローを管理すること。参照:programmers.stackexchange.com/a/154980
steveha

50

Cでは、goto潜在的なバグを特定する傾向がある現在の関数のスコープ内でのみ機能します。setjmpそして、それlongjmpははるかに危険であり、非ローカルで、複雑で、実装に依存しています。ただし、実際には、あまりにもあいまいで一般的ではないため、多くの問題が発生することはありません。

gotoC言語での危険は非常に誇張されていると思います。元のgoto議論は、昔ながらのBASICのような言語の時代に遡り、初心者は次のようなスパゲッティコードを書くことに注意してください。

3420 IF A > 2 THEN GOTO 1430

ここで、Linusは次の適切な使用法について説明しています。httpgoto//www.kernel.org/doc/Documentation/CodingStyle(第7章)。


7
BASICが最初に利用可能になったとき、ジャンプする方法として、GOTO nnnnおよびGOSUB mmmmに代わるものはありませんでした。構造化コンストラクトは後で追加されました。
ジョナサンレフラー

7
あなたは要点を逃しています...それでも、スパゲッティを書く必要はありませんでした...あなたのGOTOは常に
規則正しい

setjmp/ の動作はlongjmp、同じスコープ内の他の場所からスコープ内のスポットにジャンプする手段として使用された場合にのみ指定されたことにも注意する必要があります。コントロールsetjmpが実行されるスコープを離れると、longjmpによって作成された構造で使用しようとすると、setjmp未定義の動作が発生します。
スーパーキャット2012年

9
BASICの一部のバージョンでは、これが可能ですGOTO A * 40 + B * 200 + 30。これが非常に便利で非常に危険な方法であることを確認するのは難しくありません。
Jon Hanna

1
@Hjulleは式を計算してから、その番号のコード行に移動します(明示的な行番号は、ほとんどの以前の方言の要件でした)。ZXスペクトラムベーシックはそれを受け入れるものでした
ジョンハンナ

48

GOTO「構造化プログラミング」の人々はほとんど討論に勝ち、今日の言語は回避するのに十分な制御フロー構造を持っているので、今日、ステートメントについて大したことを見ることは困難GOTOです。

goto最新のCプログラムのs の数を数えます。今の番号を追加breakcontinuereturn声明。さらに、使用回数を追加しifelsewhileswitchまたはcase。それGOTOは、ダイクストラが手紙を書いた1968年にFORTRANまたはBASICで書いていたとしたら、プログラムが何回持っていたかということです。

当時のプログラミング言語には制御フローが欠けていました。たとえば、元のダートマスBASICでは次のようになります。

  • IFステートメントにはありませんでしたELSE。必要な場合は、次のように記述する必要があります。

    100 IF NOT condition THEN GOTO 200
    ...stuff to do if condition is true...
    190 GOTO 300
    200 REM else
    ...stuff to do if condition is false...
    300 REM end if
    
  • あなたの場合でもIF文は必要ありませんでしたELSE、それはまだ通常で構成一行が、これに限定されましたGOTO

  • DO...LOOPステートメントはありませんでした。非FORループの場合は、明示的にループを終了するGOTOIF...GOTO、最初に戻す必要がありました。

  • ありませんでしたSELECT CASE。あなたは使用しなければなりませんでしたON...GOTO

だから、あなたはになってしまった多くGOTOプログラム中の。またGOTO、1つのサブルーチン内でのs の制限に依存できなかったため(GOSUB...RETURNサブルーチンの概念が非常に弱いため)、これらGOTOのsはどこにでも移動できます。明らかに、これは制御フローに従うのを難しくしました。

これが反GOTO運動の起源です。


もう1つ注意すべき点は、ループ内にまれに実行しなければならないコードがある場合に、コードを記述する好ましい方法は420 if (rare_condition) then 3000// 430 and onward: rest of loop and other main-line code// 3000 [code for rare condition]//であること3230 goto 430です。この方法でコードを記述すると、一般的なメインラインの場合の分岐やジャンプを回避できますが、理解が難しくなります。一部の分岐が+/- 128バイトなどに制限されていて、補完的なペアがない場合(たとえば、「cjne」は存在するが「cje」は存在しない)、アセンブリコードでの分岐回避はさらに悪くなる可能性があります。
スーパーキャット

私はかつて、128サイクルごとに実行される割り込みを持つ8x51のコードを書いたことがあります。そのISRの一般的なケースで費やされる追加のサイクルごとに、メインラインコードの実行速度が1%以上減少します(128のうち約90サイクルは通常メインラインで利用可能だったと思います)。分岐命令は2サイクルかかります(分岐とフォールスルーの両方のケースで)。コードには2つの比較があり、1つは通常等しいと報告されます。他の、等しくない。どちらの場合でも、まれなケースのコードは128バイト以上離れていました。だから...
スーパーキャット

cjne r0,expected_value,first_comp_springboard/.../ cjne r1,unexpected_value,second_comp_fallthrough// `ajmp second_comp_target` // first_comp_springboard: ajmp first_comp_target// second_comp_fallthrough: ...。あまり良いコーディングパターンではありませんが、個々のサイクルが重要になると、そのようなことを行います。もちろん、1960年代には、特に最新のCPUは奇妙な最適化を必要とすることが多く、ジャストインタイムコンパイルシステムが存在しない場合に存在しなかったCPUに最適化コードを適用できるため、このような最適化レベルは今日よりも重要でした。問題のコードが書かれました。
スーパーキャット2012年

34

Go Toは、特定の場合に「実際の」例外処理の一種の代役を提供できます。考慮してください:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

明らかに、このコードはスペースをとらないように簡略化されているので、詳細にこだわらないでください。しかし、gotoの使用を避けるために不合理な長さにしようとするプログラマーによって、私がプロダクションコードで何度も見た別の方法を検討してください。

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

機能的には、このコードはまったく同じことを行います。実際、コンパイラーによって生成されるコードはほとんど同じです。ただし、プログラマーがNogoto(学術的な非難の恐ろしい神)をなだめる熱意のなかで、このプログラマーはwhileループが表す根本的なイディオムを完全に破り、コードの読みやすさについて実数を示しました。これは良くありません。

だから、物語の教訓は、自分がgotoの使用を避けるために本当に愚かなものに頼っているのを見つけたら、そうしないでください。


1
私はあなたがここで言っていることに同意する傾向がありますが、breakステートメントが制御構造の中にあるという事実は、それらが何をするかを正確に明らかにします。このgoto例では、コードを読んでいる人は、コードを調べてラベルを見つける必要があります。ラベルは理論的には、実際には制御構造の前にある可能性があります。私は古いスタイルのCを十分に経験していないので、どちらか一方の方が明らかに優れていると判断できますが、どちらの方法にもトレードオフがあります。
ビビアンリバー

5
@DanielAllenLangdon:事実breaksがループ内にあるが、彼らがいることを正確に明らかにループを抜けます。実際にはループがないので、それは「正確に彼らがすること」ではありません。その中の何も繰り返す機会はありませんが、それが最後まではっきりしていません。一度しか実行されない「ループ」があるという事実は、制御構造が悪用されていることを意味します。このgoto例では、プログラマはと言うことができますgoto error_handler;。それはより明確で、追跡するのがさらに難しくなります。(Ctrl + F、「error_handler:」でターゲットを検索します。「}」でそれを実行してみてください。)
cHao

1
航空管制システムで2番目の例に似たコードを見たことがあります。「gotoがレキシコンにない」ためです。
QED

30

Donald E. Knuthがこの質問に、本「Literate Programming」、1992 CSLIで回答しました。p。17エッセイ「gotoステートメントを使用した構造化プログラミング」(PDF)があります。その記事は他の本にも掲載されているのではないかと思います。

この記事では、ダイクストラの提案について説明し、これが有効な状況について説明します。しかし、彼はまた、構造化されたループのみを使用して簡単に再現することができない多くのカウンターの例(問題とアルゴリズム)を示します。

記事には、問題の完全な説明、履歴、例、および反例が含まれています。


25

後藤さんは参考になりました。

私は1975年にプログラミングを始めました。1970年代のプログラマーにとって、「後藤は有害だと考えられていました」という言葉は、現代の制御構造を持つ新しいプログラミング言語は試してみる価値があると言っていました。新しい言語を試しました。私たちはすぐに改宗しました。私たちは二度と戻ってこなかった。

私たちは二度と戻ったことはありませんが、あなたが若ければ、そもそもそこに行ったことはありません。

さて、古代のプログラミング言語の背景は、プログラマーの年齢の指標として以外はあまり役に立ちません。それにもかかわらず、若いプログラマーはこの背景を欠いているため、「有害であると考えられる」というスローガンが導入されたときに対象読者に伝えられていたメッセージを理解できなくなっています

人が理解していないスローガンは非常に明るくはありません。そのようなスローガンを忘れることはおそらく最善です。そのようなスローガンは役に立ちません。

しかし、この特定のスローガンである「後藤は有害だと考えられています」は、独自のアンデッドライフを引き受けました。

後藤は虐待されませんか?回答:確かですが、何ですか?実際には、すべてのプログラミング要素悪用される可能性があります。boolたとえば謙虚さは、私たちの一部が信じたいと思っているよりも頻繁に虐待されています。

対照的に、私は1990年以来、goto虐待の1つの実際の事例に出会ったことを思い出せません。

gotoの最大の問題は、おそらく技術的な問題ではなく、社会的な問題です。よく知らないプログラマーは、gotoを非推奨にするとスマートに聞こえると感じることがあります。そのようなプログラマーを時々満足させる必要があるかもしれません。それが人生だ。

今日のgotoで最悪なのは、十分に使用されていないことです。


24

答えを追加するジェイバルーに惹かれて、私は£0.02を追加します。Bruno Ranschaertがまだ行っていない場合は、Knuthの「GOTOステートメントを使用した構造化プログラミング」の記事を紹介しました。

私が議論したことを見なかった1つのことは、正確には一般的ではないが、Fortranの教科書で教えられた種類のコードです。DOループの拡張範囲やオープンコード化されたサブルーチンのようなもの(これはFortran II、Fortran IV、またはFortran 66-Fortran 77または90ではないことを思い出してください)。少なくとも構文の詳細が不正確である可能性はありますが、概念は十分に正確でなければなりません。それぞれのスニペットは、単一の関数内にあります。

Kernighan&Plaugerによる優れた、しかし時代遅れの(そして絶版の)本 『The Elements of Programming Style、2nd Edn』には、その時代のプログラミング教科書(70年代後半)からのGOTOの乱用の実際の例が含まれていることに注意してください。ただし、以下の資料はその本からのものではありません。

DOループの拡張範囲

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

そのようなナンセンスの1つの理由は、古き良き時代のパンチカードでした。ラベル(正統なスタイルであったため、順不同です)は列1にあり(実際には、列1から5になければなりません)、コードは列7から72にあります(列6は続きです)マーカー列)。列73〜80にはシーケンス番号が付けられ、パンチカードデッキをシーケンス番号順にソートするマシンがありました。シーケンスされたカードにプログラムがあり、ループの途中に数枚のカード(行)を追加する必要がある場合、それらの余分な行の後にすべてを再パンチする必要があります。ただし、1枚のカードをGOTOのものに交換した場合は、すべてのカードの再シーケンスを回避できます。ルーチンの最後に新しいカードを新しいシーケンス番号で押し込んだだけです。「グリーンコンピューティング」への最初の試みと考える

ああ、あなたも私がごまかしていて叫んでいないことに気付くかもしれません-Fortran IVは通常すべて大文字で書かれました。

オープンコード化されたサブルーチン

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

ラベル76と54の間のGOTOは、計算されたgotoのバージョンです。変数iの値が1の場合、リストの最初のラベル(123)に移動します。値が2の場合、2番目に移動します。76から計算されたgotoへのフラグメントは、オープンコード化されたサブルーチンです。これは、サブルーチンのように実行されるコードの一部でしたが、関数の本体に書き込まれました。(Fortranにはステートメント関数もありました-これは1行に収まる組み込み関数でした。)

計算されたgotoよりも悪い構成がありました-変数にラベルを割り当てて、割り当てられたgotoを使用できます。Googlingに割り当てられたgotoは、それがFortran 95から削除されたことを教えてくれます。構造化プログラミング革命に向けて1つチョークします。これは、ダイクストラの「有害なGOTO考慮済み」の手紙または記事で公に始まったと言えます。

Fortran(および他の言語では、ほとんどが道端に落ちてしまった)で行われた種類の知識がないと、初心者にとって、ダイクストラが扱っていた問題の範囲を理解するのは困難です。その手紙が発行されてから10年が経過するまで、私はプログラミングを開始しませんでした(しかし、しばらくFortran IVでプログラミングするのは不幸でした)。


2
gotoインザワイルド」を使用したコードの例を見たい場合は、「募集:ボーズヒバードソートアルゴリズムの動作」に、1963年に公開されたいくつかの(Algol 60)コードを示しています。元のレイアウトは、最新のコーディング標準とは比較できません。明確にされた(インデントされた)コードはまだかなり不可解です。gotoステートメントがないアルゴリズムが最大であるかを理解することは、(非常に)困難にします。
ジョナサンレフラー、2013年

1
パンチカードに近いものを経験するには若すぎるので、再パンチの問題について読むことは賢明でした。+1
Qix-モニカは2014

20

後藤さんが有害だと思ったことはありません。

GOTOはツールであり、すべてのツールとして、使用および乱用することができます。

ただし、プログラミングの世界には、使用されるよりも悪用される傾向のあるツールが数多くあり、GOTOはその1つです。Delphi のWITHステートメントは別のものです。

個人的に私は典型的なコードではどちらも使用していませんが、GOTOWITHの両方の奇妙な使用が保証されており、代替ソリューションにはより多くのコードが含まれます。

最良の解決策は、キーワードが汚染されていることをコンパイラーが警告するだけであり、警告を取り除くためにステートメントの前後にいくつかのプラグマディレクティブを詰め込む必要があるでしょう。

それは、はさみで走らないように子供に言うようなものです。はさみは悪くないですが、いくつかの使用法はおそらくあなたの健康を維持するための最良の方法ではありません。


GOTOの問題は、現代のプログラミング言語で当たり前とされている重要な不変条件を壊すことです。一例を挙げると、関数を呼び出す場合、関数が完了すると、通常または例外的なスタックの巻き戻しによって呼び出し元に制御が戻ると想定します。その関数がGOTOを誤用する場合、もちろんこれはもはや真実ではありません。これにより、コードについて推論することが非常に難しくなります。GOTOの誤用を注意深く回避するだけでは不十分です。GOTOが依存関係によって誤用されても、問題は引き続き発生します...
Jonathan Hartley

...したがって、関数呼び出しについて推論できるかどうかを知るために、推移的な依存関係のすべてのソースコードのすべての行を調べ、GOTOを誤用していないことを確認する必要があります。したがって、言語にGOTOが存在するだけで、自分で完全に使用した(または使用しなかった)場合でも、自分のコードについて自信を持って推論する能力が損なわれました。このため、GOTOは注意深く使用するためのツールではありません。それは体系的に壊れており、言語でのその存在は一方的に有害であると見なされています。
ジョナサンハートレー

gotoキーワードがC#から削除されたとしても、「ここにジャンプ」の概念はILにまだ存在しています。無限ループは、gotoキーワードなしで簡単に構築できます。呼び出されたコードが返されるという保証の欠如が、あなたの意見では、「コードについて推論することができない」ことを意味する場合、私たちはその能力を持っていなかったと思います。
Lasse V. Karlsen

ああ、あなたは洞察力のある私たちの誤解の原因を見つけました。gotoC#では、言葉の本来の意味での本当の「後藤」ではありません。これは、関数内でのジャンプのみを許可する非常に弱いバージョンです。「goto」を使用している感覚で、プロセスのどこにでもジャンプできます。したがって、C#には「goto」キーワードがありますが、おそらく本当のgotoはありませんでした。はい、実際のgotoはILのレベルで利用できます。これは、言語がコンパイルされてアセンブリにコンパイルされる場合と同じです。しかし、プログラマーはそれから保護されており、通常の状況ではそれを使用できないため、カウントされません。
ジョナサンハートレー

ちなみに、ここで説明する意見は、もともと私のものではありませんが、ダイクストラの1967年の原著の中心であり、編集者によって「有害と見なされる」と改名されています。それは非常に革命的で洞察に富み、広く受け入れられていたからです。
Jonathan Hartley

17

あなたが自分で考えられる限り、それは決してありませんでした。


17

Linuxカーネルでいくつかのことを始めたので、gotosは以前ほど気になりません。最初は、彼ら(カーネルの人たち)がコードにgotoを追加したのを見て、ちょっと怖かった。それ以来、私は、いくつかの限られた状況で、gotoの使用に慣れてきました。そして、今では自分でそれらを使用することになります。通常、これは関数の最後にジャンプして、ある種のクリーンアップと救済を実行するgotoであり、同じクリーンアップと救済を関数の複数の場所に複製するのではありません。そして通常、それは別の関数に引き渡すのに十分な大きさではありません-たとえば、ローカルで(k)mallocされた変数を解放するのが典型的なケースです。

setjmp / longjmpを1回だけ使用するコードを作成しました。それはMIDIドラムシーケンサープログラムにありました。再生はすべてのユーザー操作とは別のプロセスで行われ、再生プロセスはUIプロセスと共有メモリを使用して、再生に必要な限られた情報を取得しました。ユーザーが再生を停止したいとき、再生プロセスは、ユーザーが停止したいときに実行されていた場所を複雑に巻き戻すのではなく、longjmpを「最初に戻す」だけでやり直しました。それは素晴らしく機能し、シンプルであり、その場合、私はそれに関連する問題やバグを抱えていませんでした。

setjmp / longjmpには場所があります-しかし、その場所はあなたが訪れる可能性は低いですが、非常に長い間一度です。

編集:私はちょうどコードを見ました。実際に使用したのはlongjmpではなくsiglongjmp()でした(大したことではありませんが、siglongjmpが存在することさえ忘れていました)。


15

CでVMを作成している場合、次のように(gccの)計算されたgotoを使用することがわかります。

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

ループ内の従来のスイッチよりもはるかに高速に動作します。


標準ではないという唯一の問題ですよね?
カルマリウス'19年

&&op_inc(左)&は左辺値を期待するため、コンパイルは行われませんが、(右)&右辺値を生成します。
fredoverflow

7
@FredO:これは特別なGCCオペレーターです。しかし、wtfの状況を確実に理解できないので、このコードはほとんどの状況で拒否します。
パピー

これはgotoと暗号コードの両方の使用を正当化する1つの例(速度の最大最適化)ですが、最適化の必要性、大まかに機能する方法、および行ごとの最適な方法を説明するために、コメントを増やす必要があります。できるコメント。ですから、それでも失敗しますが、メンテナンスプログラマーが暗闇にいるためです。しかし、私はVMの例が好きです。ありがとう。
MicroservicesOnDDD

14

goto紛らわしいメタプログラミングに使用できるため

Goto高レベルレベルの両方の制御式であり、その結果、ほとんどの問題に適した適切な設計パターンがありません。

それの低レベルのgotoが実装のような高い何かという原始的な操作であるという意味で、whileまたはforeachか何か。

それだ高レベルの特定の方法で使用される場合、それは明確な順序で実行されるが、中断されない様式で、構造化ループを除いてそのコードを取るという意味で、それは十分であるロジックの断片にそれを変更gotoS、グラブ-動的に再構築されるロジックのバッグ。

だから、そこにある平凡なの側がgoto

平凡な側面は、上向きのジャンプが完全に合理的なループを実装することができ、下向きのジャンプが完全に合理的に行うことができることであるbreakまたはreturn。もちろん、実際のwhilebreakまたはreturn貧しい人間がの効果をシミュレートする必要はないとして、たくさん読みやすくなりgoto大きな画像を得るために。したがって、一般的には悪い考えです。

悪の側はしばらく、休憩、または復帰のためにはgotoを使用していないルーチンを含むが、と呼ばれるもののためにそれを使用してスパゲッティロジック。この場合、goto-happy開発者はgotoの迷路からコードの一部を構築しています。それを理解する唯一の方法は、全体として精神的にそれをシミュレートすることです。elseつまりif、がの正反対ではないコードを評価する問題を想像してみてください。ネストされたifsは、外部によって拒否されたものなどを許可する場合がありますif

最後に、主題を本当にカバーするために、アルゴルを除く本質的にすべての初期の言語は当初、バージョンに応じて単一のステートメントのみを作成したことに注意する必要がありif-then-elseます。したがって、条件付きブロックを実行する唯一の方法はgoto、逆条件付きを使用してブロックを回避することでした。非常識ですが、古い仕様をいくつか読んだことがあります。最初のコンピューターはバイナリマシンコードでプログラムされていたことを思い出してください。そのため、あらゆる種類のHLLが命の恩人だったと思います。彼らが得たHLL機能の正確さについては、あまり厳格ではなかったと思います。

goto私がすべてのプログラムに1つを使用するのが常であったことをすべて言って、私は「純粋主義者を困らせるためだけに」書いた。


4
純粋主義者を困らせるための+1!:-)
ルミ

10

プログラマーにGOTOステートメントの使用を拒否することは、大工が釘を打っているときに壁に損傷を与える可能性があるため、ハンマーを使用しないように指示するのと同じです。実際のプログラマーは、GOTOを使用する方法と時期を知っています。私はこれらのいわゆる「構造化プログラム」の背後を追跡しましたが、GOTOの使用を回避するためだけにそのようなHorridコードが表示され、プログラマーを撃つことができました。さて、反対側を守るために、実際のスパゲッティコードも何度か見ました。それらのプログラマーも撃たれるべきです。

これが私が見つけたコードのほんの一例です。

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

-----------------------または----------------------

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10

3
CRTは正しいですか?(Y / N): ':YORN =' Y 'またはYORN =' N 'になるまで入力YORN; など
joel.neely

5
実際、しかしより重要なことに、実際のプログラマーは- を使用しない場合gotoとその理由を知っています。$ programming_guruが言ったので、タブー言語の構成要素を回避することは、それがカーゴカルトプログラミングのまさに定義です。
Piskvorが

1
それは良いアナロジーです。素材に損傷を与えずに釘を打ち込むには、ハンマーを取り除かないでください。むしろ、ネイルパンチと呼ばれる単純なツールを使用します。これは、先が細くなっている金属製のピンで、先端に中空のくぼみがあり、釘の頭にしっかりと結合します(これらのツールは、釘ごとにサイズが異なります)。ネイルパンチのもう一方の鈍い端をハンマーでたたきます。
Kaz


7

元の論文は、「無条件のGOTOは有害と見なされる」と考える必要があります。特に、初期のコードに共通するテストとジャンプではなく、条件付き(if)と反復(while)の構造に基づくプログラミングの形式を推奨していました。goto適切な制御構造が存在しない一部の言語または状況では、依然として役立ちます。


6

Goto 使用できるのは、エラーに対処する必要がある場合だけであり、エラーが発生するたびに特別な処理が必要です。

たとえば、リソースを取得してセマフォまたはミューテックスを使用している場合、それらを順番に取得する必要があり、常に逆の方法で解放する必要があります。

一部のコードでは、これらのリソースを取得する非常に奇妙なパターンが必要です。これらのリソースの取得と解放の両方を正しく処理してデッドロックを回避するために、簡単に保守および理解できる制御構造を作成することはできません。

gotoなしでいつでも正しく実行できますが、この場合と他のいくつかの場合、Gotoは実際には主に可読性と保守性のための優れたソリューションです。

-アダム


5

最近のGOTOの使用法の1つは、C#コンパイラが、yield returnで定義された列挙子の状態マシンを作成することです。

GOTOは、プログラマーではなくコンパイラーによって使用されるものです。


5
コンパイラは誰が作成したと思いますか?
tloach

10
もちろんコンパイラー!
Seiti 2008年

3
彼は「GOTOはコンパイラーによって発行されたコードによってのみ使用されるものである」という意味だと思います。
Simon Buchan

これは反対のケースgotoです。goto列挙子を実装するために手動でコード化された状態マシンで使用する場合は、yield代わりに使用できます。
Jon Hanna

多くの文字列をオンにし(if-elseへのコンパイルを防止するため)、デフォルトの大文字と小文字のコンパイルでgotoステートメントで切り替えます。
M.kazem Akhgary 2015

5

CおよびC ++(他の犯人の中で)が改行にラベルを付けて続行するまで、gotoは引き続き役割を果たします。


したがって、ラベル付きのbreakまたはcontinueはgotoとはどのように異なりますか?
Matthew Whited、

3
それらは、制御フローの完全に任意のジャンプを許可しません。
DrPizza

5

GOTO自体が悪である場合、コンパイラはJMPを生成するため、コンパイラは悪です。コードのブロックにジャンプする場合、特にポインタを追跡することが本質的に悪ければ、RETurn命令は悪です。むしろ、悪は虐待の可能性にあります。

時々私はいくつかのオブジェクトを追跡しなければならないアプリを書く必要があり、各オブジェクトはイベントに応答して複雑な一連の状態に従う必要がありましたが、全体は間違いなくシングルスレッドでした。疑似コードで表される場合、典型的な状態のシーケンスは次のようになります。

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

これは新しいことではないと思いますが、C(++)で処理する方法は、いくつかのマクロを定義することでした。

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

次に、(状態が最初は0であると仮定して)上記の構造化状態マシンが構造化コードに変わります。

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

これのバリエーションにより、CALLとRETURNが存在する可能性があるため、一部のステートマシンは他のステートマシンのサブルーチンのように動作できます。

珍しいですか?はい。メンテナの側でいくつかの学習が必要ですか?はい。その学習は報われますか?私はそう思う。ブロックにジャンプするGOTOがなくても実行できますか?いいえ。


1
恐怖から言語機能を回避することは、脳損傷の兆候です。これは地獄よりも気の利いたものです。
Vector Gorgoth 2014

4

同僚/マネージャーが間違いなくコードレビューで、または偶然見つけたときに、その使用に疑問を投げかけるので、私はそれを避けます。私はそれが用途(たとえば、エラー処理の場合)を持っていると思いますが、何らかのタイプの問題を抱える他の開発者を非難します。

それはそれだけの価値はありません。


C#のTry ... Catchブロックの良い点は、例外が例外ハンドラーにバブルアップするときに、スタックと他の割り当てられたリソース(スタックの巻き戻しと呼ばれる)をクリーンアップすることです。これにより、GotoよりもTry ... Catchの方がはるかに優れているため、Try ... Catchがある場合は、それを使用してください。
スコットウィットロック

私はより良い解決策があります:ポップアウトしたいコードのチャンクを 'do {...} while(0);'でラップします。ループ。そうすれば、try / catchループのオーバーヘッドなしでgotoと同じ方法でジャンプアウトできます(C#についてはわかりませんが、C ++ではtryは低コストで、catchは高コストなので、単純なジャンプで十分な場合に例外をスローするのは過度です)。
Jim Dovey、

10
ジム、それの問題は、それが後藤を取得する愚かな回り道の方法に過ぎないことです。
スタイルを使用したコーディング

4

実際、私はgotoを使用せざるを得なかったのです。なぜなら、このコードを書くためのより良い(より速い)方法を文字通り考えることができなかったからです。

複雑なオブジェクトがあり、そのオブジェクトに対して何らかの操作を行う必要がありました。オブジェクトが1つの状態にある場合は、操作のクイックバージョンを実行できます。それ以外の場合は、操作のスローバージョンを実行する必要がありました。ことは、遅い操作の途中で、高速操作でこれが可能であることが理解できたということでした。

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

これは、速度が重要なリアルタイムUIコードに含まれていたため、正直に言って、GOTOが正当化されたと思います。

ヒューゴ


GOTO以外の方法は、fast_calculations関数を使用することです。これにより、オーバーヘッドが発生します。おそらくほとんどの状況では目立たないでしょうが、あなたが言ったように、これは速度が重要でした。
カイル・クロニン

1
まあそれはほとんど驚くべきことではありません。絶対に異常なパフォーマンスガイドラインを持つすべてのコードは、常にほとんどすべてのベストプラクティスを破ります。ベストプラクティスは、到達可能性と保守性のためのものであり、さらに10ミリ秒を節約したり、5バイトのRAMを節約したりするためのものではありません。
Jonathon 2014年

1
@ JonathonWisnoski、gotoの合法的な使用により、変数のネズミの巣と干渉して非常に大量のスパゲッティコードを排除し、どこに向かっているかを追跡できます。
フォンブランド、2015年

4

gotoを使用できるほとんどすべての状況で、他の構成を使用して同じことができます。Gotoはとにかくコンパイラーによって使用されます。

私は個人的にそれを明示的に使用することはありません。


4

ここでの回答のどれから私が見たことがないことの1つは、「goto」ソリューションは、しばしば言及される構造化プログラミングソリューションの1つよりも効率的であるということです。

多くの入れ子になったループのケースを考えてください。多数のif(breakVariable)セクションの代わりに「goto」を使用する方が明らかに効率的です。「ループを関数に入れてreturnを使用する」という解決策は、多くの場合完全に不合理です。ループがローカル変数を使用している可能性が高い場合は、関数パラメーターを介してループをすべて渡す必要があります。これにより、そこから生じる余分な頭痛の負荷を処理する可能性があります。

次に、私が頻繁に使用しており、多くの言語で使用できないtry {} catch {}構造の原因であると思われるほど一般的であるクリーンアップケースについて考えてみます。同じことを実行するために必要なチェックと追加の変数の数は、ジャンプを行うための1つまたは2つの命令よりもはるかに悪く、追加の関数ソリューションはまったくソリューションではありません。それがより扱いやすく、より読みやすいとは言えません。

多くのプログラマーにとって、コードスペース、スタックの使用状況、実行時間は多くの状況で十分ではないかもしれませんが、2 KBのコードスペースしか使用しない組み込み環境では、明確に定義されたものを避けるために50バイトの追加の命令があります。 「goto」は笑えるだけで、多くの高レベルのプログラマーが信じているほど珍しい状況ではありません。

「goto is harmful」という記述は、常に過度に一般化されていたとしても、構造化プログラミングへの移行に非常に役立ちました。この時点で、私たちは皆、それを(私たちがすべきように)使用することに警戒するのに十分なほどそれを聞いています。それが明らかに仕事に適したツールであるとき、私たちはそれを恐れる必要はありません。


3

深くネストされたループから抜け出すためにそれを使用できますが、ほとんどの場合、深くネストされたループなしでコードをリファクタリングしてよりクリーンにすることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.