ループと関数をサポートする言語で「goto」を使用することは有利ですか?もしそうなら、なぜですか?

gotoの皆さん Edsger Dijkstra の GoTo は有害と見なされる記事を直接的または間接的に引用して、彼らの立場を立証しています。残念ながら、ダイクストラの記事には事実上何もありません goto の方法で行う ステートメントは最近使用されているため、記事で述べられていることは、現代のプログラミングシーンにはほとんど、またはまったく適用できません. goto -less meme は現在、高位から口述された聖典、その大祭司、認識された異端者の回避 (またはさらに悪い) に至るまで、宗教に迫っています。

Dijkstra の論文を文脈に当てはめて、この主題に少し光を当てましょう。

Dijkstra が論文を書いた当時、人気のある言語は、BASIC、FORTRAN (初期の方言)、およびさまざまなアセンブリ言語などの構造化されていない手続き型言語でした。高水準言語を使用している人々がコードベース全体をジャンプすることはよくあることでした 「スパゲッティコード」という用語を生み出した、ねじれた、ゆがんだ実行スレッドで。これは、Mike Mayfield によって書かれた古典的な Trek ゲームに飛び乗って、物事がどのように機能するかを理解しようとすることで確認できます。少し時間をとって、それをよく見てください。

これ ダイクストラが 1968 年の論文で非難していたのは、「go to ステートメントの無制限の使用」です。これ 彼がその論文を書くようになったのは、彼が住んでいた環境です。コード内の好きな場所にいつでもジャンプできる機能は、彼が批判し、停止するよう求めていたものでした。それを goto の貧血力と比較すると C やその他のより現代的な言語で使用することは単純に危険です。

異端者に立ち向かうカルト信者たちの高らかな詠唱がすでに聞こえてきます。 「しかし」彼らは「goto を使用すると、コードを非常に読みにくくすることができます」と唱えます。 C." そうそう? goto なしではコードを非常に読みにくくすることができます。 同じように。このように:

#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
            _-_-_-_
       _-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_
            _-_-_-_
}

goto ではありません 見えるので、読みやすいはずですよね?または、これはどうですか:

a[900];     b;c;d=1     ;e=1;f;     g;h;O;      main(k,
l)char*     *l;{g=      atoi(*      ++l);       for(k=
0;k*k<      g;b=k       ++>>1)      ;for(h=     0;h*h<=
g;++h);     --h;c=(     (h+=g>h     *(h+1))     -1)>>1;
while(d     <=g){       ++O;for     (f=0;f<     O&&d<=g
;++f)a[     b<<5|c]     =d++,b+=    e;for(      f=0;f<O
&&d<=g;     ++f)a[b     <<5|c]=     d++,c+=     e;e= -e
;}for(c     =0;c<h;     ++c){       for(b=0     ;b<k;++
b){if(b     <k/2)a[     b<<5|c]     ^=a[(k      -(b+1))
<<5|c]^=    a[b<<5      |c]^=a[     (k-(b+1     ))<<5|c]
;printf(    a[b<<5|c    ]?"%-4d"    :"    "     ,a[b<<5
|c]);}      putchar(    '\n');}}    /*Mike      Laman*/

いいえ goto そこにも。したがって、読み取り可能でなければなりません。

これらの例のポイントは何ですか?読めない、保守できないコードを作るのは言語機能ではありません。それを行うのは構文ではありません。これを引き起こすのは悪いプログラマーです。そして、上記の項目でわかるように、下手なプログラマーは何でもを作ることができます 言語機能が読み取れず、使用できません。 for のように そこにループします。 (見えますよね?)

公平を期すために、一部の言語構造は他の構造よりも悪用されやすいです。ただし、あなたが C プログラマーなら、#define の使用の約 50% をもっと詳しく調べます。 gotoに対する十字軍に行くずっと前に !

ですから、ここまで読んでくださった方のために、注意すべき重要な点がいくつかあります。

<オール>
  • goto に関するダイクストラの論文 ステートメントは goto のプログラミング環境用に作成されました 多かった アセンブラではない最新のほとんどの言語よりも潜在的に有害です。
  • goto のすべての使用を自動的に破棄する これは、「一度は楽しもうとしたが、気に入らなかったので、今は反対している」と言うのと同じくらい合理的だからです。
  • 現代の (貧血) goto の正当な使用法があります 他の構成要素で適切に置き換えることができないコード内のステートメント。
  • もちろん、同じステートメントの違法な使用があります.
  • godo」のような最新の制御ステートメントの違法な使用もある " abomination where always-false do ループは break を使用して分割されています goto の代わりに .これらは goto の賢明な使用よりも悪いことがよくあります .

  • 私が認識している「goto」ステートメントを使用する理由はいくつかあります (これについては既に説明した人もいます):

    関数をきれいに終了する

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

    ネストされたループの終了

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

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

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

    これらすべての例で、goto は単一の関数の範囲に制限されていることに注意してください。


    盲目的にベスト プラクティスに従うことは、ベスト プラクティスではありません。 goto を避けるという考え方 フロー制御の主な形式としてのステートメントは、判読できないスパゲッティ コードの生成を回避することです。適切な場所で控えめに使用すれば、アイデアを表現する最も単純で明確な方法になる場合があります。 Zortech C++ コンパイラと D プログラミング言語の作成者である Walter Bright は、それらを頻繁に、しかし慎重に使用しています。 goto でも ステートメント、彼のコードはまだ完全に読み取り可能です。

    結論:goto を避ける goto を避けるため 無意味です。本当に避けたいのは、読めないコードを生成することです。 goto の場合 ロードされたコードは読み取り可能であり、問​​題はありません。