Entries

スポンサーサイト
[EDIT]
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

-件のコメント

コメントの投稿

新規
非公開にする

-件のトラックバック

トラックバックURL
http://harukazehajime.blog115.fc2.com/tb.php/130-8394f9a7
この記事に対してトラックバックを送信する(FC2ブログユーザー)
構造化プログラミング
[EDIT]
前回は非構造化プログラミングについて実例を使ってみてみましたが、ほかのパラダイムのサンプルがないので比較のしようがありませんでした。今回は構造化プログラミングを、前回のBASICと比べながら見てみます。
言語は引き続きcbasです。というのも、cbasはMinimal BASICを前提としながら構造化とオブジェクト指向に対応しているからです。
いきなりで申し訳ないのですが、階乗を求めるプログラムは以下の通りです。

call main


sub main
num = getFactNumber
max = num


num = fac(num)
print str$(max) + "'s factorial is " + str$(num)
quit
end sub


sub getFactNumber
while
input "Please input factorial number >$", num$
wend num$ = ""
getFactNumber = val(num$)
end sub


sub fac(i)
if i <= 1 then
fac = 1
return
endif
i = i * fac(i - 1)
fac = i
end sub

こんな感じです。別ウィンドウで前回のコードと比較してみてください。やってることは同じなんですが、長くなったわりにはすっきりしたコードに見えるはずです。前回のコードの方が見やすいという人はFORTRANあたりでも苦労することはないでしょう。とはいえ、人気のあるプログラミング言語は進化を続けていて、命令型言語はどれも構造化・オブジェクト指向化の道をたどっているのが現状です。

それはさておいて、順を追って解説しましょう。今回は行番号がないので、コードの先頭を1行目とします。
1行目はサブルーチンコールです。サブルーチンとは、ソースコードを小分けにして再利用しやすくしたもの、サブルーチンコールはサブルーチンに飛んでいく、いわばGOTOです。
call mainとあるのでsub mainまで飛んでいきます。cbasの場合sub ~ end subまでがサブルーチンになります。sub xxx というのはサブルーチンコールしたときの目標です。前回のコードで言えば行番号と同じような役割をしているわけです。

mainサブルーチンの1行目は数値変数numにgetFactNumberを代入しています。が、コードの先を見ていくと、もう一つgetFactNumberが見つかります。subではじまっているので、サブルーチンです。
構造化プログラミングでは、=演算子で代入するときにサブルーチン名を見つけると、サブルーチンコールして処理した結果を直接代入することができます。

では途中にあるコードを飛ばして先にgetFactNumberサブルーチンを見てみましょう。
getFactNumber サブルーチンの1行目にはwhileとあります。これはwendまでに書かれたコードを繰り返し実行するためのキーワードで、while文やwhileループと呼びます。
その次の行に、前回のコードでも出てきたinputがありますね。ユーザーの入力を待つ命令です。

その次の行にwendがあります。ここまでがループの本体となります。後ろにあるnum$ <> ""は見覚えがあるでしょう。前回のプログラムではこんなふうになっていました。
20 IF NUM$ = "" THEN 105
構造化プログラミングでは、たいていこのように、IF(if文)だけに頼ることなく条件判定できるのも特徴のひとつです。
ということは、wend num$ <> ""は空文字であるかどうかの判定をしています。
wendはIFの一種とも言えますから条件判定を行います。ただし、IFとは逆の挙動を示します。IFでは条件が正しいときにTHENまでを実行しますが、wendはIFで言えば、指定した条件が正しくないときにwhileに戻っていきます。したがってnum$ <> ""が正しくない(num$が空でない)と判断するとwhileに戻っていきます。正しいと判断すると、ループを抜け出しwendの次の行に戻っていきます。
前回のコードは該当部分だけを抜粋するとこんな感じでした。

5 LET FLAG = 0 : LET FAC = 1 : LET I =1
10 INPUT "Please input factorial number >", NUM$
20 IF NUM$ = "" THEN 105
105 LET FLAG = 1
125 IF FLAG = 1 THEN 5

処理が一致する部分に同じ行番号を付けて今回のコードを見ると

while
5, 10 input "Please input factorial number >", num$
125, 20 wend num$ <> ""

前回のコードに当てはめると、実行する順番は10→20→125→(5)行目です。125行目は強制的にTHENの後ろまで飛ばされますが。

ではいよいよ次の行に進みましょう。
getFactNumber = val(num$)
はてさて何だこれは。サブルーチンに対して値を代入しているか、サブルーチンとnum$を整数に変換した値を代入しているかのように見えます。これはcbas特有の構文で、サブルーチンを関数のように値を返すものにしたい場合、このようにして戻り値を与えます。
サブルーチンを抜け出したとき、mainサブルーチンの1行目、
num = getFactNumber
においてgetFactNumberに与えておいた値がnumに代入されます。

end subはサブルーチンの終わりです。構造化プログラミングの大多数はこのようにend subなどでサブルーチンを抜け出すか、returnで抜け出します。returnはサブルーチンの途中で何らかの理由で抜け出したいとき、また大部分のプログラミング言語ではサブルーチンの戻り値をreturnに渡します。

getFactNumberサブルーチンを抜けるとmainルーチンの2行目に移ります。変数maxに入力された値が入っているnumを代入しています。前回の30行目のコード
30 LET MAX = VAL(NUM$)
にあたりますが、サブルーチンを抜け出すときにVALで数値に変換しているので、VALがなくなっています。

1行開けて、やっと階乗を求める部分です。
num = fac(num)
どうやら、facというサブルーチンに、3行前で入力された値を与えて(渡して)、それを同じ変数に代入しているようです。というわけでsub fac(i)まで飛んでみましょう。
sub fac(i)
sub xxxがサブルーチンの移動先というのは解説していましたが、括弧の中身までは説明していませんでしたね。
括弧の中身は、先ほどnum = fac(num)の括弧で与えられた値が入っています。つまり i にはnumの値が入っています。この i を引数といいます。
その次は前回も見た、1以下かどうかを判定するIF文ですね。しかしthenの後ろが改行になっていて複数行にまたがっています。構造化プログラミングでは、thenからendifまでを複数行にまたがらせることができます。この中では i が1以下だったときの処理をしていて、サブルーチンの戻り値を強制的に1にしています。returnはサブルーチンを途中で抜け出す命令でした。
i が2以上であればthenからendifまでを飛ばし、endifの次の行に移動します。
ここまでを前回のコードと比較してみましょう
前回:

40 IF MAX <= 1 THEN LET MAX = 1 ELSE GOTO 50
45 GOTO 210
50 LET FAC = FAC * I
55 LET I = I + 1
60 IF I > MAX THEN GOTO 210 ELSE GOTO 50
210

今回:

40 if i <= 1 then
40 fac = 1
45 return
50 endif
50..60 i = i * fac(i - 1)

コードの実行順序は、i が1以下のときは40→40→45で、2以上のときは最初の40→50..60です。
endifの次の行が階乗を求める本体です。i とfacサブルーチンの値とを乗算して、facサブルーチンを呼び出します。このときの引数は i から1を引いた値です。i が1になるまで繰り返されます。サブルーチンの中で自分のサブルーチンを呼出し、頭から実行し直すことを「再帰」といいます。
動作を、変数の中身を見ながら追ってみましょう。i は4とします。それと矢印は関数や変数が返す値を表しています。

3←fac(4 - 1) : REM すべての i = i * fac(i - 1)
2←fac(3 - 1) : REM が先に実行される
1←fac(2 - 1)
if i <= 1 then→true
1←fac = 1
end sub
sub fac(2(i) - 1)
2←i 2← i * 1←fac()
2←fac = 2← i
end sub
sub fac(3(i) - 1)
6←i = 3← i * 2←fac()
6←fac = 6← i
end sub
sub fac(4)
24←i = 4← i * 6←fac()
24←fac = i
end sub

分かりやすくなったでしょうか?かえってわかりにくい?
まずは再帰呼出しになっているfac()をひたすら繰り返し、引数が 1 になったところで(3行目)if i <= 1 thenがtrueになり(4行目)、戻り値として1を返して(5行目)end sub(6行目)で1つ上のsub fac()の再帰呼出し部分に戻ります(7行目=3行目)。ここで i = i * fac(i -1)の i * fac()を実行します(8行目)。fac()の戻り値が1で、このサブルーチンに入ったときに実行されるsub fac(i)の i に与えられた値が i = i * fac(i -1)のすべての i に入っています。サブルーチンの引数を受け取る変数(ここでは i)を用意すると、その名前の変数すべてに引数が割り当てられるからです。

ということで一番最初の再帰呼出しで4 - 1が与えられていますが、一番最後に呼び出された再帰を抜け出すと3行目の状態(i = 2)から継続されます。fac i = i * fac(i -1)は i(2) = i(2) * fac()になっています。facは1を戻り値として返しているのでつまりi = 2(i) * 1(fac())です(8行目)。これを戻り値としてサブルーチンを抜け出します(fac = 2)。
同じようにしてサブルーチンを抜け出しsub fac(3 - 1)(11行目=2行目)のサブルーチンを続行します。fac i = i * fac(i -1)は i = i(3) * fac()でfacの戻り値は2ですから i =3(i) * 2(fac())と等価です。そしてfac = 6として再帰を抜け出します。
最後に再帰の一番上に帰ってきます。一番最初のサブルーチンに与えた引数はsub fac(4)でした。したがってfac i = i * fac(i -1)は i = i(4) * fac()で、facの戻り値は6ですから i = 4(i) * 6(fac())です。これを実行してfac = 24、end subで抜け出してmainに戻り、num = fac(num)を実行します。左辺のnumには24が代入され、printではれて答えが表示され、quitでプログラムを抜け出します。

最後までおつきあいありがとうございました。いかがでしょう?あまり複雑なプログラムではないので実際のところどっちがいいかわからないかもしれませんね。
関連記事

0件のコメント

コメントの投稿

新規
非公開にする

0件のトラックバック

トラックバックURL
http://harukazehajime.blog115.fc2.com/tb.php/130-8394f9a7
この記事に対してトラックバックを送信する(FC2ブログユーザー)

Appendix

プロフィール

さくらゆーな

Author:さくらゆーな
鉄道熱が再燃して、撮影に模型にいろいろやってます。
最近反核運動に偏ってるのを反省したいけど
知れば知るほど極悪非道な界隈で止まらない…

カレンダー

03 | 2017/04 | 05
- - - - - - 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 - - - - - -

+ アーカイブ
 

最近の記事

カテゴリー

検索フォーム


キーワード

カウンター

トータルカウンター
現在の閲覧者数:

ads3

Mac ソフトのことなら act2.com

Make a donate

もしこのブログを気に入っていただけたら上記アフィリエイトプログラムか下のPayPalでブログ・サイトの維持にご協力ください。

donationPrice

ブロとも申請フォーム

この人とブロともになる

ブログランク

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。