goto単語

ゴートゥ
  • 2
  • 0pt
掲示板へ

gotoとはプログラミング言語の構文のひとつであり、その使用を巡って論争が絶えないことで有名である。

概要

古典的にはgotoは条件ジャンプを行うための構文であり、その名の通り「あそこの(あのラベルの)処理に飛べ!」という単純明解な機である。 C言語のgotoを基準にすると、同一関数内である限り、ラベルが書ければどこにでもジャンプすることが出来、ジャンプ先から処理を継続する。ジャンプ元には特にgotoなどで定されない限り戻ってこない

gotoをサポートする言にはFORTRAN, BASIC, C (当然C++も)と、メジャー古参高級言が名を連ねており、いかにも「由緒正しきスタンダードな機です」感が漂うが、現在では実行速度を重視する分野などを除けば、条件ジャンプができるgotoは好ましくない機とする意見が流を占めている。

なお、GoD言語は"goto"を採用しているが、表記こそ"goto"であるものの、後述のように用途制限により実質的には"古典的gotoの代替"となっており、古典的gotoの議論とは関係である。

以降では、古典的gotoに関する議論を中心に取り上げる。

なぜ批判されるか

gotoは関数呼び出しと違って呼び出した場所に戻って来る保がないので、プログラムの構造をぶっちぎって構造化プログラミングを台しにすることが出来てしまう。 ある意味構造化プログラミング敵ともいえる。

状況によっては特定の処理を飛ばす事も容易に行えてしまう。 たとえば、「ファイルを開き、データ読み込み、ファイルを閉じる」という処理があった時、「データ読み込む」処理にgotoが入っていて、gotoで飛んだ先から「ファイルを閉じる」処理に戻ってくるように書くのを忘れてしまうと、ファイルが開いたままになってしまう。

また、安易なgotoの濫用は絡まり合うような処理の流れを生み、本人ですら処理の流れを把握することが困難なソースコードを作り出す。 絡まり合っていることからスパゲティコードと呼ばれる。

歴史

gotoの誕生

gotoの誕生は最古の高級プログラミング言語FORTRANの登場(1957年)にまで遡る。現代のFortran仕様訂を経て現役のプログラミング言語として一部の分野では活躍を続けているが、最初期のFORTRANサブルーチンすらなく(ただし、1958年FORTRAN IIではサブルーチンと関数定義がサポートされている)フロー制御にgotoは必須であった。

もっとも、当時はプログラミング言語といえば機械語アセンブラしかなく、メモリソースコードの長さすら切り詰めねばならないほど少なく、できることも限られていたため、プログラムに構造をもたせる余裕も必要もなかったと言える。

gotoへの批判の誕生

その後、ハードウェア進化と共にプログラムも複雑さを増していくつれて、前述のようなgotoの弊立つようになり、ついには最短経路問題で有名なダイクストラらの1968年の論文「Go To Statement Considered Harmful (Go To構文は有だと考えられる)」で批判されるまでになった。

なおC言語の誕生が1972年であり、上記論文はそれより

goto擁護論の誕生により論争時代へ

gotoのある言語におけるgotoの擁護

構造化定理により、gotoを用いた処理を反復・分岐などの組み合わせに書き換えることは理論上可であるが、その書き換えを行うことで性可読性について不利になるケースもある。

後述のようにC言語では例外処理がないために、goto構文に頼らざるを得ない局面が存在する。

また、gotoのある言の多くは多重ループからの脱出する方法がgotoに頼らざるを得ない(gotoがあるから、多重ループ脱出のための仕組みがいらなかったとも言える)言仕様になっている。

その後

gotoのある先発言語

C言語仕様訂でもgotoの性質は変化しなかったが、多くのプロジェクトコーディング規約などでgotoの使用方法を制限するようになった。

C++はCとの互換性の関係からgotoが存在するが、例外処理機構を持っている(実装されたのは1990年よりも後の話だが)ため例外処理のためにgotoを使う局面はない。

後発言語

Javaを始めとするC言語より後のプログラミング言語の多くは、後述のように"古典的gotoの代替"を用意することで、条件ジャンプとしてのgotoの採用は見合わせている。

余談だが、JavaC言語の反からgoto文を採用していないが、万が一後世でgotoの必要性が立された時のためにgotoを予約としてキープしている。goto議論の呪縛は根深い。

論争の本質

「gotoがないよりはあったほうがプログラムを書くときに便利だが、弊が生じやすい」という点ではおそらく全員意見が一致している。

賛否が別れるのは、一つの問題点を、優先するものの違う2つの立場から捉えているからに過ぎない。すなわち、

  • goto肯定:
    • やれることの選択肢は広い方がよく、弊を生じるような使い方をしなければよい。
    • が生じたとしたらそれは使った者の責任であり、言仕様責任ではない。
  • goto否定:
    • 使えなければ、使用による弊が起き得ないから、多少不便でも言仕様の段階で使えないようにしよう。
    • 仕様で使える場合でも、コーディング規約などで使用を制限・禁止しよう。
    • 自分が注意していても他のかがやらかす性もあり、それを防ぐのも言仕様(または規約)の責任である。

という立場である。

もっとも、近年のプログラミング言語の傾向としては(優秀なプログラマーからそうでない者まで)大人数で安全な開発を行えるようにすることを重視するという考えが流なので、今時の言古典的意味でのgotoを採用することはまずない。

議論がかみ合わない点

概要で述べたように、gotoはどこにでも飛ぶことが出来る一方で戻ってくることが保されないために、注意しないと構造化プログラミングを破壊してしまうことが問題なのである。

逆にいうと、goto使用もプログラムの構造を破壊しない限りにおいては構造化プログラミング上の問題を生じない。gotoを使うことが「必ず」問題を引き起こすというわけではないのである。gotoの文字列をにした途端に思考停止してアンチパターンだと糾弾するのはいいことではない。

一方で、gotoを否定する議論では、goto使用による弊という「結果」のみを問題にしているわけではない。「gotoが存在すること」によって弊のある使い方が「出来てしまう」こと自体が問題視されているのである。従って、弊を起こさないように上手に使いさえすればよいというgoto肯定の反論も的を射ていない。可能性を生み出しただけでアウトなんだよ

また、gotoを適切に用いれば構造化プログラミング上は問題ないといっても、コード中にgotoが存在するだけで、そのgotoの適切性を検証しなければならないという管理上の問題が生じる。この場合、gotoを利用している単位での品質管理が重要で、継続してテストコードが書かれ検証されるようにすることであらかじめ問題を検知しておきたい。

そのような環境が整わないのであれば、やはり、gotoが使用可な言であっても使用しないほうがよいだろう。例外があるとしたら、コメントなどで適切性を簡潔に明できる場合や、達人が自分一人プログラミングする場合くらいであると思われる。

古典的gotoの代替

上記のようにgotoでジャンプすることは絶対悪ではなく、構造化プログラミングを妨げないことが保されるならジャンプも有益でありえると言える。

この点に対して、C言語より後の較的新しいプログラミング言語の多くは条件ジャンプとしてのgotoではなく、より限定された範囲でのジャンプ制御命(break, continue, 例外処理, etc.)を代替として提供していることが多い。そういった言では条件ジャンプとしてのgotoが必要になるケースはほぼない。

例外処理(try-catch-finally)

(tryで始まるブロックの中で)規定通りの動作ができないときに例外(Exception)を発生(throwする)させる。例外が発生すると、(tryブロックの直後にある)例外を捕捉(catch)する節までジャンプする。

例外処理は同一関数内という制限を受けずスタックを遡上して関数外に出ること(大域脱出)が可という意味ではC言語のgotoよりも強な機である。その一方で、ジャンプ先はcatch節だけという限定を受けている上、「ファイルを閉じる」のような処理まではスキップしてしまわないように(catch節の直後にある)finally節で実行されることが保されており、構造化プログラミングを破壊しないための安全機構が設けられている。

C++にはfinallyがないが、デストラクタなどを用いて実行の保が可である。

break, continue

for文から抜け出すbreak, continueジャンプの一種であり、古典的gotoの代替と考えることもできる。ジャンプ先はループの末尾に限定されている。C言語でもサポートされているため、これらがないがためにgotoが必要になるケースは少ない。

初期のFORTRANループの脱出にgotoを用いていたが、Fortran 90ではbreak, continueに相当する機サポートされた。

もっとも多重ループからの脱出には、Fortran 90でも、C言語でも、C++でも、gotoに頼るのが定石の一つとして認められている(なお例外処理があれば、gotoを使わずとも多重ループからの脱出は可能な気がする)。

Java以降の言ではbreak, continueラベルを使えるものが多いため、gotoがなくとも多重ループからの脱出は可である。

C言語における"制限された"goto

C言語のように例外処理がない言ではgoto文を用いずに高い可読性を実現出来ないことがあり、goto文を使用すべきとしているプロジェクトも存在する。

しかし、後述の事例に記したように、基本的には上記で述べた例外処理と同等のフローを実現するために使用されている場合がほとんどである(歴史的に考えれば、このエラー処理フローを言レベルサポートしたものが先述の例外処理であると言った方が適切かもしれない)。これらの場合、言仕様上は制限されていなくても、コーディング規約としてgotoのジャンプ先に実質的制限が加えられていると見ることもできる。

制限の強まった"goto"

先述のように、新しい言でも"goto"を採用している言はある。 これらの言のgotoは較的C言語に近い機を有しているが、使用するための制約を強くしたり条件で処理をスキップしないようにするなど、C言語よりも自由度や機を制限したものとなっている。

D

D言語のgotoはC言語と同様に同じ関数の内部のラベルしか参照出来ないようになっている。 それに加えてgotoによって変数の初期化をスキップするようなジャンプは禁止され、またジャンプする場合であってもfinally節の実行は回避されないようになっている。

Go

Goでもgoto構文は採用されており、やはりC言語と同様に同じ関数の内部のラベルしか参照出来ない。 加えてgotoする時点でスコープに入っていない変数ジャンプ先ではスコープに入っているような(i.e. 初期化をスキップする)ジャンプは禁止され、またより内側のブロックへのジャンプも出来ない仕様となっている。

GOTO 関連動画

GOTO 関連項目

参考: goto使用に関するC言語の事例

C言語によるオープンソースプロジェクトでは、適した場所(例外処理など)に限るという前提ではあるがgotoを利用しているプロジェクトは非常に多い。

gotoが適すると思われるような例であってもgotoを利用しないという場合もなくはないです。

【スポンサーリンク】

  • 2
  • 0pt
スマホ版URL:
https://dic.nicovideo.jp/t/a/goto

この記事の掲示板に最近描かれたお絵カキコ

お絵カキコがありません

この記事の掲示板に最近投稿されたピコカキコ

ピコカキコがありません

goto

17 ななしのよっしん
2015/05/29(金) 12:37:12 ID: h8t/dAIoBw
条件的ジャンプ権は内閣により閣議決定されました
18 ななしのよっしん
2015/11/20(金) 09:45:28 ID: mT1ingmRrQ
Goto is god
19 ななしのよっしん
2016/02/27(土) 23:24:06 ID: 78weVXldaj
まぁ上に飛ばなければそんなに問題はないんじゃね?

それかINTERCALとかにあるcomefromを使うか…
しかもある実装によるとラベルに対応するcomefromが複数あるとそのラベルに達した時点で複数のスレッドが生成されて行して実行を続けるという…
20 ななしのよっしん
2016/06/13(月) 14:52:02 ID: yQ7vCGP5L0
ここまで後藤なし
21 ななしのよっしん
2017/10/25(水) 23:06:24 ID: d3mqZgMYH1
goto検索して一番goto批判的なのが処の記事だった。
構造化を破壊するgotoが好ましくないだけで、例えば、

if(…) goto work_tail;

で処理ブロック丸々飛び越してネスト地獄を減らすのは、可読性においても有効な手なんだけど、それを引き継いだ馬鹿が何でもかんでもgotoを乱用する可性が怖いので、わかってる人たちだけで使いましょうねってことだよね?
記事を書いた人は何が何でもgoto全否定義者なようで、ああこの人は日常においてもtruefalseの二択なんだな~と思わずにはいられない。
22 ななしのよっしん
2017/10/25(水) 23:23:55 ID: d3mqZgMYH1
ちなみに良心的gotoについては、近代の高級言であれば、witch-caseや例外、ラベル付きbreakなどの代替処理命が存在しますが、
逆に言えば、肯定と否定はじつは同じコーディングスタイルの命名の付け方で対立しているに過ぎないという言い方もできます。
良心的gotoに準じる命は使っても良いというお付きですね。
lintでも、以前は機械的にgotoを排除していましたが、最近は良心的gotoに限り、使用を許すようになりました。
23 ななしのよっしん
2017/11/28(火) 00:33:52 ID: 6R3swkvKUy
>>21
true, falseの二択だとしても条件が杜撰じゃなければ問題ないんだけどな

まぁ使わなくても問題ないことのが多いし、逆にいえば使うべき場所をしっかり定義しとくべきといったところか
たしかにgoto読みづらいからきちんとコメントでなぜ使ったのか、使った場合はどうする意図があったのかを的確に明示しとかんと、まじで作った本人しか理解できないコードになるから、大人数での共同作業では使わないほうが難だってことだね

つーか別に個人で使う分にはまったく問題ないしな
プログラムなんて動きゃいいんだよ動きゃ!
24 ななしのよっしん
2018/11/26(月) 22:13:26 ID: JhUBz9xMFu
全体的に稿してみた(Markdown Extra: https://hackmd.io/tAFGVkGYTCijbiAHLPanZAexit)

DとかGoとかあんまり触ったことないんですけど、見る限りCのgotoにとても近い印を受けました。
実際その辺りの言を使ってる人的には如何です?
25 ななしのよっしん
2018/11/27(火) 23:33:23 ID: JhUBz9xMFu
クローズソースだと分からんけどgoto使ってないオープンソースプロジェクトが見つけられなかった
26 deadbull
2018/11/30(金) 07:08:36 ID: dB2pOv6et6
>>24
検討させていただきました。
記事に具体性がない部分があるために断定は出来ませんが、いわゆる例外処理(try-catch構文)がない言(C言語)で、例外処理を行うためにはgotoがないと可読性が悪くなるのでgotoが必要というお立場のようにお見受けします。

その上で確認したいのですが、例外処理以外の局面でgotoの不使用が可読性パフォーマンスを落とす場合というものはありますでしょうか。
心当たりがないならないで結構ですので、あれば「どの言の」「どのようなケース」かご教示いただければ幸いです。