その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

用法用量を守って楽しいgotoを!

お疲れ様です。
最近どうですか。goto文書いてますか!?


最近、goto文をガンガン使う昔ながらのコードを読み、更に僕自身もgoto文をガンガン書きましたので、
goto文について思うところを書いていきたいと思います。

goto文が禁止されている背景

「(恥ずかしい)goto文禁止!!」というプロジェクトのコーディング規約が採用されている所は多いと思います。
僕の周りでもそういった規約が適用されているプロジェクトは多いようです。


さて、なぜgoto文は禁止されているのでしょうか?

  • 構造化がくずれる!! 危い!!!
  • スパゲティプログラムが出来上がる!!
  • そもそもgoto文なんて使わなくても、所望の機能は実現できんじゃね!?
  • なんかよくわかんねーけど、危ないっぽいから禁止じゃ!!!

大まかに分けて以上の理由でしょうか? もっとあるかもわかりませんが。

構造化がくずれる!! 危い!!!

  • プログラムの入口は1つ。そして出口も1つ
  • 順次・選択・反復の3つの構文で制御する
  • プログラム全体を機能単位に分割する。更にその分割した単位を更に分割して記述……を繰り返してプログラムを構成する
  • 上に示した機能単位の入口も1つ。出口も1つ
  • 機能単位の連結及び組み合わせを行う場合も順次・選択・反復の3つの構文を用いる

という以上の項目が、エドガー・ダイクストラ氏によって提唱された構造化プログラミングであり、
今日ではこれが理想的な構造化プログラミングとして広く認知されている事と思います。
これらを満足すれば「効率が良くてミスの少ないプログラムを記述できる。ね、簡単でしょ?」となるわけです(多分)。


つまり、この理想的な構造化プログラミングは「goto文使うんじゃねえ!!!!!」と言っている訳です。
確かにその通りかもしれません。


しかし、例えば
1) 常に実行されているjavaのプログラムが、その状況に応じてメッセージを投げる
2) そのメッセージを別のプログラム(例えばjavascriptなんか)がキャッチして、そのメッセージに応じた動作を行い、結果をjavaのプログラムに投げ返す
3) 1),2)の繰り返し
というようなプログラムはどうでしょうか。
goto文こそ使っていないものの、このプログラムはgoto文に似通っていると言えないでしょうか。
つまり、構造化が崩れるから危険、という理屈だけでgoto文を規制するのは少々厳しいような気がします。

スパゲティプログラムが出来上がる!!

これはもっともなご意見だと思います。
僕もソースコードを解読する上で、このスパゲティには中々苦戦させられました……

例えば

foo:
	printf("foo\n");
	goto qux;
bar:
	printf("bar\n");
baz:
	printf("baz\n");
qux:
	printf("qux\n");

というプログラムであれば"foo"の出力後に"qux"が出力される事は一目でわかると思いますが、

	goto baz;
foo:
	printf("foo\n");
bar:
	printf("bar\n");
	goto qux;
baz:
	printf("baz\n");
	goto foo;
qux:
	printf("qux\n");

というプログラムになると若干厄介になってきます。
gotoの行き先をずっと辿って行かなくては、全体の動きが分からないのでメンテナンスのコストがグッと向上します。
この程度の規模のプログラムであればまだ大丈夫だとは思いますが、そもそもこの程度の規模のプログラムなんて現実には無いでしょう……
ちなみに以下のようにすると

foo:
	printf("foo\n");
bar:
	printf("bar\n");
	goto foo;
baz:
	printf("baz\n");
qux:
	printf("qux\n");

ファッキンデッドロック!!!!!


以上から、goto文は「処理Aをやってから処理Dを行い、処理Bを行い処理Cに遷り、終了処理を行う」のように
或る特定のシークエンスを実現する為に使用するのではなく、
1番目の例のように処理と処理の間に存在する処理をスキップする為に利用するのが良いと思いました。
つまり、「上方向へのgoto文はよせーっ!!」

そもそもgoto文なんて使わなくても、所望の機能は実現できんじゃね!?

仰るとおり(かもしれません)。


ただ、「条件文によってフラグを変化させて、そのフラグの内容に応じて実行する処理を選択する」
みたいなプログラムを記述する時に、その条件が複雑である場合はgoto文で書いた方が簡単で良いかもしれません。
また、繰り返し文のネストが深い時、その最深部で例外処理を行う必要が生じた場合は
条件に応じてbreak、そしてまた条件判断してbreak、break, break...とbreak祭を開催するよりは
goto文で書いた方がすっきりして良いかもしれません。
(ただこの場合の「ネストが深い」はどれくらい深い場合でしょうか。5個以上とかでしょうか?)


あと、C言語でredo(Perlなんかの)を手っ取り早く実装するときなんかも良いかも分かりません。


もちろん、「goto文を使わないと所望の機能が実現できねえんだよ!」という時はgoto文を使わざるを得ないと思いますが。

なんかよくわかんねーけど、危ないっぽいから禁止じゃ!!!

確かにその認識のうちは、goto文は使わない方が確かに良いような気がします。
ただ、Linuxのカーネルにはgoto文が結構使われておりますよ。
という事はLinuxも危険! というかOSが危険!
でも、常に身の危険を感じながらOSを利用していないですよね? つまり……


僕個人としては平気でメモリの領域をぶっ壊すポインタの方が危険な気がしなくもないわけですが……

結論

  • トレースが面倒になるので、goto文の「多用」は避けた方が無難
  • よりスマートに要求された昨日の実装が出来るのであればgoto文も選択肢に入れて良いのでは
  • 上方向のgoto文禁止!


といった感じでしょうか。
むしろ、プロジェクト全体のコーディング規約として「goto文を禁止」を採用するよりも、
「上方向のgoto文は記述しない事」や「1つの関数で利用しても良いgoto文は1つまで」みたいな感じで
「goto文規約」を制定して運用した方が良いのでは無いでしょうか。
流石に、「goto文禁止」が明言されている状態でgoto文を使うとカウボーイ扱いされるのは避けられないと思われるので……


という感じの事を、goto文を読み、goto文を書きながら思うなどしましたとさ。


用法用量を守って楽しいgotoを! Enjoy! :-D