Goto文
2021-06
プログラミング言語におけるgoto文(ゴートゥぶん、goto statement)とは、手続き列中の指定された場所(専らラベルで指定される)に無条件にジャンプ(移動)する、という制御構造のひとつを表現するための「文」である。古い文献などで "go to" と離していることもあるのは、英語の go to どこそこ、といったような言い回しとの類似のためでもあり、FORTRANではプログラム中の空白は基本的に無視されるので、goto でも go to でも同じだったからという理由もある(BASIC処理系の中にも、どちらも使えるようにしているものもある)。
goto文は、それ単体では働きを持たせることはできず、必ずラベルか何かによってジャンプ先が必要である。機械語における分岐命令の一種である無条件分岐と、アセンブリ言語のラベルをそのまま手続き型プログラミングに持ち込んだものと言え、何らかの条件分岐との組み合わせによって、ループであるとか、コードブロックの飛び越しであるとかいったような機能の一部となることができる。
「以前に実行した場所に戻る」というループ用途なのか、それともこの先にあるコードは別の流れの一部なので「単に飛び越したいだけ」というジャンプ用途なのか、という単純なことすら、goto文ではその見かけからは分からない。if文やwhile文と比較して、goto文はコードの意図が不明瞭になるため、後述のように、if文やwhile文で単純に書けることであれば、gotoは使うべきではない。一方で、深く多重にネストしたループからの脱出などgotoを使ったほうが単純かつ明確に書ける場合もありJava(gotoが使えない言語)のラベル付きbreakなどは、そのような場合においてgoto無しでも簡潔に書けるようにするためにある。、特にC言語においてはgotoを教条的・無条件に排除するべきではない。
資源の確保と解放などのために、プログラム片の入口と出口が必ず対応している必要がある、といった場合などは、gotoに限らず、いわゆる「途中return」等の他の多くの技法も制限されるので、以下のgoto文の議論とはきちんと区別すべきであるし、またそういった場合の制限を全てのコーディングに適用するべきではない。
なお、冗談半分ではあるが、ジャンプ先のほうにcome fromという文を置き、ジャンプ元にラベルを置く、という「comefrom文」の提案もある。提案自体はジョークだが、同等の機能がある言語は実際にいくつかあり、プログラムの理論上の研究対象にもなっている。
次のように、ラベルの付いた文と併用するC言語以外のことも考えれば、一般的にはラベルのみあれば良く、文は絶対に必要というわけではない。ラベル単体では存在できず「ラベル付き文」とする必要があるのはC言語の単なる構文上の制限である。。
goto文によって、そこからclearラベルの付いた文にジャンプし、そこから処理を続ける。
Cなどの構造化プログラミング言語においては、goto文はむやみに使ってはいけないというルールが、プロジェクト等のガイドライン等として定められていることがある。これは、goto文が原始的で自由度の高すぎる機能であるため、安易なgoto文の使用はプログラムの構造化を妨げ、デバッグなどが行いにくくなり、バグの発生や、メンテナンス性・可読性の低下の原因になりやすいからである。
しかし、多重にネストしたfor文やif文、while文などでエラー処理や例外処理などが複雑になる場合はgoto文を使った方がプログラムがすっきりと書けるケースもあるため、プログラムの構造を熟知したプログラマが状況に応じて使い分けるものとされている。
エラー処理や例外処理、RAIIなどの仕組みが充実している言語の場合、goto文が必要になるケースが少ない。この傾向はC言語よりも後発のオブジェクト指向言語で顕著である。そのため、言語仕様として始めから用意されていないケースもある。こうした言語としては、Javaなどが挙げられるJavaの場合、gotoを不要とするために充実しているのは、ラベル付きbreakとcontinueであり、例外処理機構はgotoよりも大きなスコープでのジャンプ(C言語で言えばgotoではなく、setjmp/longjmpに相当)である。。一方では、PHPで、2009年にリリースされたバージョン5.3からgotoが追加されたように、高度化する言語構造の中でgoto文が一定の再評価を受ける例もある。
BASICの様な言語や下位水準の制御言語ではgoto文は不可欠であり、goto文を利用しないと分岐やループを使ったプログラムが記述できないものもある。しかし、拡張されたBASICの中には、goto文がほとんど不必要になってしまっているものもある。
前述のように、goto文の安易な使用はプログラムの可読性を著しく低下させる。こうした可読性の低いコードのことを、制御構造が複雑に絡まっているという意味を込めて、スパゲティコードと呼ぶことがある。
「構造化プログラミング」を提唱していたコンピュータ科学者らの一人であったダイクストラは、1968年に“Go To Statement Considered Harmful"(Go To 文は有害とみなされる)という刺激的なタイトルの記事を国際学会ACMの学会誌Communications of the ACMに投稿しただし、本人が付けたタイトルはA Case Against the Goto Statementという穏便なもので、少なくともタイトルの過激さと、レターとして発表を急いだことは、編集を担当していたヴィルトによるものである。、論争を巻き起こした。こんにちでは構造化プログラミングに関してその名前だけが最もよく知られている論争となっている。その内実は言葉の有名さの割には全く知られていないし、内容や意味の理解は時代の変遷によって、かなり難しくなっていることも確かである。クヌースの言葉を借りるならば、「go to 文除去の話の二番目の場面は,多くの人たちが第一幕だと思っている事実である。」。
また considered harmful というフレーズはその後、しばしば似たような深刻な、あるいは軽妙な論争に関して付けられるものとなっている。
“Go To Statement Considered Harmful"の趣旨は次の通りである。
人間が時間によって変化するプロセスを把握する能力は、プログラムの静的な関係を把握する能力に比べて劣っているため、静的なプログラムテキストの構造と時間によって変化するプロセスの構造をなるべく近づけるのが重要である。
そのためにはプロセスの進捗を表す簡潔な指標が必要である。指標とは例えば実行中の行番号・その時点でのスタックトレース・行番号とループを回った回数のペアなどである。
例えば、1人が部屋に入るごとにカウンタを増やしていくプログラムにおいて、カウンタが室内の人数-1である瞬間はどこにあるのかという問いに答える際に、簡潔な指標があればすぐ答えられるが、簡潔な指標がなければ正確な答えは難しくなる。
goto文を無条件に許してしまうと簡潔な指標は得られなくなってしまうため、上位水準のプログラミング言語においてはgoto文は廃止するべきである。
その一方でクヌースの指摘によると、ダイクストラは“Structured Programming"においてはgoto文には触れていない。実際に“Structured Programming"においては“sequencing should be controlled by alternative, condtional and repetitive clauses and procedure calls, rather than by statements transferring control to labelled points."との1文があるのみでそれ以外では触れていない。
また、「3つの基本構造」(後述)についても、“Go To Statement Considered Harmful"においては“I do not claim that the clauses mentioned (引用者註: 順次・分岐・手続き呼び出し・反復) are exhaustive in the sense that they will satisfy all needs"とし、「3つの基本構造」には固執していない。
「構造化プログラミングの観点からgoto文を使うのは望ましくない」ものの「単にgoto文を使わなければ見通しが良くなる」という考えは“Go To Statement Considered Harmful"でも否定されており、goto文の有無のみに固執するのは不毛である。構造化プログラミングの本質の一つは、大きなプログラムを小さな構造、すなわちサブルーチンや関数に分割し、プログラムの正しさを証明することである。
「goto文有害説」は、ほとんどの場合、「構造化定理」(Structured program theorem)に結びつけて主張されたものだと信じられている。構造化定理は、
連接 (sequence): 文1; 文2
選択 (selection): if 式 then 文1 else 文2 fi
繰返し (iteration): do 文 od
という、「3つの基本構造」によって、フローチャートで記述された計算可能な関数は、全て表現できる、というものである。この定理の意味する所は、チャーチ=チューリングのテーゼと同じようなものである。すなわち、計算可能な関数について、それを計算するチューリングマシンを構成することもできるし、ラムダ計算によって計算することもできるし、帰納的関数として定義することもできる、といったことと同様に、フローチャートでも、あるいは「3つの基本構造」の組み合わせによってでも表現できる、ということである。
そのようにして表現されたものが人間にわかりやすいか否かについては触れられていないにもかかわらず、「この理論に従えばgotoを除去できる」(これは正しい)、「ゆえにわかりやすいプログラムになる」(これは正しくない)というのが、「goto文有害説」であるとして信じられるようになったため、そういったような記述がしばしば見られる。
構造化定理に従って「gotoの無いプログラム」に書き換えたプログラムが実際のところどのようなものであるのかは、英語版記事の Structured program theorem#Single-while-loop, folk version of the theorem に示されている。クヌースは「go to 文を用いた構造的プログラミング」の中で、これと同様のプログラムを示し「これですべての goto 文を除去できたわけであるが,実際にはすべての構造を失ってしまっている.」と述べ、構造化定理が示すことは、全くの「非構造化プログラミング」であることを警告した。
一方、goto文を使わずに3つの基本構造による代替を行うと、理論上は同値であっても実際にはプログラムの実行速度や記憶容量の点で性能が劣化する場合があることを示し、goto文を擁護する意見もあった。
ACMの年次大会(1972)の討論会では、トップダウンでプログラムを分割するよりgoto文を使う方が適した事例があり、証明には関心がないという意見(William Wait)や、goto文を残せば選択の余地があるが、無くしてしまうと必要になったとき困るだろうという意見(Guy Steel)などが出された。
条件文とgoto文を正しく使うことで、FORTRANでもALGOLの基本機能を実現できることを例に挙げ、プログラマは必要な機能がプログラミング言語で直接表現できないとき、どのように実装するか工夫できるべきであるという主張もあった(S.W.スリア)。
また、ドナルド・クヌースは「go to 文を用いた構造的プログラミング」(『文芸的プログラミング』収録)の中で、いくつかのgotoを使ったほうが良いだろうという例を挙げているが、その中には、再帰呼出しのある正当なプログラムを、正当な変形によってループによるプログラムに書き換えた結果「ループの中に飛び込むgoto」になる、といったような例も挙げている。
現在C言語を除く主流派の言語では、そのままのgoto文はほとんど見られなくなった。替わりにbreak文、continue文、もしくは例外処理のような特殊脱出これらは「去勢されたgoto」とも呼ばれるが、例外処理は実際には関数呼び出しをまたいだ大域脱出もできるのだから、gotoよりも例外処理のほうが強力である。をサポートし、単純な制御構造だけでは弱いと考えられる部分を補っている。さらにクロージャやコードブロック、継続のような強力な制御機構を使い、抽象度の低いgoto文を使う必要性を低くした言語もある。例えばHaskellにおいてはモナドを利用して例外や非決定性計算などの様々な制御構造を表現できる。またSmalltalkやIoにおいても制御構造はブロックを扱うメソッドとして表現している。Scheme等でサポートされている継続は「引数付きgoto」と呼ばれることもある。
一方で、例えば1999年から設計が開始されたD言語はgoto文を含んでいる。2000年に最初のバージョンがリリースされたC#もgoto文を持っている。PHPにも2009年にリリースされた5.3において制限された形ではあるがgoto文が追加された。
それまでの職人芸的なプログラミングの時代から、構造化プログラミングというパラダイムの提唱によって、2021-06。
なお、論文“Go To Statement Considered Harmful"は、その半ば挑戦的な題名がプログラミング以外の様々な分野に及んで評判となり、それをもじった様々な “〜 Considered Harmful" といった論説やジョークが生まれた(Considered harmful)。極めつきは、何かが有害であることだけが強調される題名の有害性を指摘した“`Considered Harmful' Essays Considered Harmful"であろう。同じくにおいてダイクストラは、Millsによって矮小化されたgoto文廃止という概念のもと、IBMが用語としての「構造化プログラミング」を盗用したと語っている。
ダイクストラの著書は、分かりにくいことと誤解を招きやすいことで定評がある。それを憂いたディビット・グリースは、ダイクストラ流のプログラミングについて体系化した書籍「プログラミングの科学」(The Science of Programming) を書いた。
1990年1月15日に起きた、AT&Tの長距離電話網の停止 (crash) は、break文による脱出先の勘違いによるものであった。List of software bugs#Telecommunicationsも参照。
そもそもソフトウェアの制御パステストを十分に実施していなかったせいで不具合が発見されなかったことが障害につながったのだが、適切な名前のラベルを用意し、ループ脱出にgotoを使っていれば避けられたバグでもあった。
アップルのSSL/TLS実装で発見された脆弱性CVE-2014-1266には、goto failという通称がある。goto文の誤った記述により、本来必要な署名検証処理が実行されずに処理成功として扱ってしまうというものであった。
ただし、このバグを正確に表現すると以下のような原因の複合であり、gotoは「たまたまそこにあった文がgotoだった」と捉えるのが普通のプログラマの感覚である。すなわち、
if文の帰結節に、ブロック(「複文」)だけでなく、「式文」などの単なる「文」も書ける、というALGOLやC言語の構文
そのために、インデントによる見た目と、真の構造が一致していない、というミスリード状態に気が付きにくいこと
そのために、1個目のgoto文により、一見では到達不能コードに見える2個目のgoto文が、到達可能だったこと
そのために別の、セキュリティ的に絶対に実行されなければならないコードが、到達不能コードになっていたこと
2個目のgoto文は、到達不能コードだと思うのならば、放置せずに削除しなければならなかったこと
一方で、2個目のgoto文は実際には到達不能コードではなかったので、第三者には、その部分を見ただけでは、削除して良いのか判断不可能だったこと