Goに「goto」ステートメントがあるのはなぜですか


110

Goに「goto」という文があることに驚きました。「goto」ステートメントは過去のものであり、それがプログラムの実際のフローをふさいでしまうために邪悪であり、関数またはメソッドは常にフローを制御するより良い方法であると私は常に教えられてきました。

私は何かを逃しているに違いない。なぜGoogleはそれを含めたのですか?


5
gotoステートメントが本当に必要な場合があります。後藤は無差別に使用した場合にのみ悪です。たとえば、gotoステートメントを使用せずに有限状態マシンパーサーを作成することが、不可能ではないにしても非常に難しい場合。
xbonez

5
これはGoに固有のものではありませんが、言語がステートメントを保持する理由についての適切な議論、およびその使用に対する反対意見については、この投稿をチェックしてください。質問にはいくつかの良い参考文献がリンクされています。編集:ここに別のものがあります。
Cᴏʀʏ

3
提供されたSOディスカッションを介してOPを回避するために、LKMLに関するディスカッションを以下に示します。LKML gotoは、特定の場合になぜ有用であるかを要約しています。@Kissakiの答えを勉強して読んでください。
kostix


スタックを保存してから再開したい場所に戻る、継続パターンの実装に役立ちます。
ジャスティンデナハワー

回答:


78

Go標準ライブラリのソースコードを実際にチェックすると、gotosが実際に適切に適用されている場所を確認できます。

たとえば、math/gamma.goファイルでは次のgotoステートメントが使用されます。

  for x < 0 {
    if x > -1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }
  for x < 2 {
    if x < 1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }

  if x == 2 {
    return z
  }

  x = x - 2
  p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6]
  q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7]
  return z * p / q

small:
  if x == 0 {
    return Inf(1)
  }
  return z / ((1 + Euler*x) * x)
}

gotoこの場合、単に制御フローのために使用される別の(ブール)変数を導入するから私たちを保存し、最後にをチェック。この場合gotoステートメントにより、コードが実際に読みやすくなり、追跡が容易になります(gotoあなたが述べた反対の意見に反して)。

また、gotoステートメントには非常に具体的なユースケースがあることに注意してください。goto言語仕様では、スコープに入ってくる(宣言されている)変数を飛び越えたり、他の(コード)ブロックに飛び込んだりすることはできません。


69
あなたの例では、なぜsmall(x,z)代わりに呼び出す関数を導入しないのですか?そうすれば、small:ラベルでアクセス可能な変数について考える必要がなくなります。goには、コンパイラーでの特定のタイプのインライン化サポートがまだ欠けているためだと思います。
Thomas Ahle 2013年

5
@ジェスタ:それは私たちが可視性とスコープを持っているものですよね?
トーマスアーレ2014

6
@ThomasAhle Goではgoto、新しい変数が導入された後はラベルを指すことができません。「goto」ステートメントを実行しても、gotoの時点ではまだスコープ内になかった変数がスコープに入ってはなりません。
km6zla 2014

4
@ ogc-nickすみません、わかりませんでした。関数は必要なスコープで宣言できるため、関数を必要としないコードからは見えません。後藤とスコープについては話していませんでした。
Thomas Ahle 2014

4
@MosheRevah参照されるコードは、読みやすさを考慮して最適化されていません。単一の関数内で22行にわたるgotoを使用して、生のパフォーマンス用に最適化されています。(そして、トーマス・アーレの提案は私の目にもっと読みやすいです。)
joel.neely

30

Gotoは、組み込みの制御機能のいずれもが望みどおりの動作を行わない場合、およびGotoを使用して必要な内容を表現できる場合に適しています。(gotoがない場合、一部の言語ではこれらのケースでは残念です。最終的に、ブールフラグを使用したり、gotoよりも悪い他のソリューションを使用したりして、制御機能を悪用することになります。)

他の制御機能(かなり明白な方法で使用される)が希望どおりの動作を実行できる場合は、gotoよりも優先して使用する必要があります。そうでない場合は、太字にしてgotoを使用してください。

最後に、Goのgotoには、いくつかのあいまいなバグを回避するために設計されたいくつかの制限があることに注意してください。仕様のこれらの制限を参照してください


7

Gotoステートメントは、60年代と70年代のスパゲッティコードの時代以来、多くの信用を失っています。当時、ソフトウェア開発の方法論は非常に貧弱でした。しかし、Gotoは本来悪意はありませんが、もちろん、怠惰なプログラマーや熟練していないプログラマーによって悪用されたり悪用されたりする可能性があります。乱用されたGotoに関する多くの問題は、チームのコードレビューなどの開発プロセスで解決できます。

goto同じ技術的な方法で、ジャンプがあるcontinuebreakreturn。これらは同じように悪であると主張することもできますが、そうではありません。

GoチームがGotosを組み込んだ理由は、それが一般的なフロー制御プリミティブであるという事実が原因であると考えられます。さらに、彼らはうまくいけば、囲碁の範囲がばか安全な言語を乱用することを不可能にすることを除外すると結論付けました。


continuebreak、そしてreturn一つのキーの特定に非常に異なっている:彼らは唯一の「囲みスコープを残す」を指定します。彼らは奨励するだけでなく、開発者がコードの構造を検討し、構造化プログラミングプリミティブ(ループ、関数、スイッチステートメント)に依存することを明示的に要求します。gotoステートメントの唯一の節約は、コンパイラのオプティマイザがタスクに達していないときにHLLでアセンブリを記述できることですが、これは可読性と保守性を犠牲にします。
パルティアンショット

これがそう行くに見つけることは驚くべきことである理由は、golangの開発者は、構造化プログラミングパラダイムと「例外フロー制御」との選択肢を持っていた他のすべての場合には/命令ポインタ操作は好きなことをされsetjmplongjmpgoto、そしてtry / except / finally彼らの側に誤ることにしました注意してください。goto、fwictは、事前「構造化プログラミング」制御フローに対する唯一の黙認です。
パルティアンショット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.