なぜ????
ifとgotoが基本要素だから
やいのやいの言われてかわいそうなgotoちゃんですが, gotoはとても重要な命令を移動するという機能があります。ほかのforとかは結局gotoちゃんの特殊化された構文ですから, 当然gotoに書き換えることができます。
ifには分岐の機能がありますが実はこれも条件に一致していたらgotoするという機能なので条件分岐を除けばgotoです。
なので、普段意識はしないと思いますが, ちょっと頭の体操がてら, 変換していきましょう。
というわけで, この記事では各構文をifとgotoだけで書き出してみることにします。
この記事では各要素を次のように表します
- cond … 条件: (a == 1のようなやつです)
- 大文字のA,B等 … 一般的な文やブロックです
- if (cond) { A } ... condの条件と一致していたらAを実行します
- goto a; … aというラベルにジャンプします
- label a: … aというラベルをこの位置に貼ります。(gotoと合わせて使用します)
- if (cond) goto a; ... condの条件と一致していたらラベルaへジャンプします。
その他はC言語ライクに書いています。
if文
まず初めによく見るif文を条件式とgoto文で変形してみましょう。
ifは一般的にこの形になると思います。
以下condを条件式, 大文字のAやBを一般的な式や文とします。
if ( cond ) {
A
}
B
これをgotoで書くとこうなります。(!はcondの否定)
if ( !cond ) goto b;
A
label b:
B
!なくても書けますがAとBの順番を保存するならこうなります。
次からは, if{A}は使用せずif gotoのみ使用します。
if - else文
if - else文はこうですね。
if ( cond ) {
A
} else {
B
}
C
これは飛ばす場所を条件で決めてあげればいいのこうなります。
if( !cond ) goto b;
A
goto c:
label b:
B
goto c:
label c:
C
ではここからいろいろなものをif-gotoで分解していきます。
do - while文
なぜ最初がdo - while文かというと簡単だから
do {
A
}while(cond);
condがtrueの間はdoのところに戻ればいいので
label a:
A
if(cond) goto a;
while文
while文はdo-whileと違って最初に条件式を走らせないといけないので最初に条件式のところにジャンプさせます
while(cond){
A
}
変換すると
goto f;
label a:
A
label f:
if( cond ) goto a;
for文
for(int i=0;i<length;i++)などでおなじみのfor文ですが実際には最初の式と最後の式は何でもよく2番目の式が条件式なので次の形が一般形です。(ここでexprは任意の式です)
for( expr1; cond; expr2){
A
}
これをまずwhileに変換してみます。
こうですね
expr1;
while(cond){
A
expr2;
}
これをそのまま先ほどのwhileに適応して
expr1;
goto f;
label a:
A
expr2;
label f:
if( cond ) goto a;
switch文
switch文はifの塊なので難しくないでしょう(コンパイラなどでは頑張って最適化かけてたりしますが, 一番単純な形にします。)
switch (value){
case v1:
A
break;
case v2:
B
break;
}
はifが2つと考えれば
if(value == v1) goto a;
if(value == v2) goto b;
goto l:
label a:
A
goto l:
label b:
B
goto l: //省略可能ですが, A側にあるのでこちらにもつけておきます
label l:
関数
関数も結構頑張るとできます。
void a(){
A
}
B
foo();
C
a()に移動して, Aが完了してから元に戻ればいいので, a'というラベルをaを呼びだすところの次に置けば行けそうですね。
こうなります。
label a
A
goto a':
B
goto a
label a':
C
いやいやちょっと待てよと思うかもしれません。
a()を呼ぶところが2つとか3つになったら・・・とかんがえるとこれでは不完全な気がします。
それを実現するにはa'が可変である必要がありますよね。
ですので, 次の命令を追加します。
- push a; 今いる場所を記録してラベルaにgotoする。
- pop; 最後にpushされた場所に戻って次の行から実行する。
label a:
A
pop;
B
push a;
C
これはgotoなのか?と言われると今回は割愛しますが, gotoの飛び先をラベルではなく, プログラムの行など動的に変えられるようにした1パターンなので, まぁgotoということで
なんの役に立つの?
あなたもプログラマなら一度は簡単なスクリプトとか, インタプリタとか無性に作りたくなっちゃったりするときが来ると思うのでそのときに役に立つかもしれないし立たないかもしれない