−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
応用数学II 1995.10.27/28
12.プロシージャという概念(1)SUB文 飯島
−配列という概念−
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
12−1 配列
実は,今までも配列という概念は使ってきている。言及しなかっただけのことだ。まず
は,次のプログラムを入力し,実行してみよう。
CLS
DIM x(100) ← 配列を定義している。
FOR i = 1 TO 100
x(i) = RND
NEXT
FOR i = 1 TO 100
PRINT x(i);
NEXT
PRINT
max = 0
FOR i = 1 TO 100
IF x(i) > max THEN max = x(i)
NEXT
PRINT "max = "; max
そう,ここでは,「最大値」を求めています。このプログラムの最大の利点は,データ
が100 個あっても,FOR ... NEXT によって,数行で済ませてしまうことができる点にあ
ります。このように,
決まりきった作業を,複数のデータに行いたい場合
には,配列を定義し,
... x(i) ...
というように「変数」を使って記述することができると便利なわけで,そういう目的で,
「配列」という概念があり,また実装されているわけです。
12−2 配列の大きさ
さて,上では,100個のデータを処理していましたが,試しに,200個に修正して
みましょう。上記のプログラムのどこを直したらいいですか。
そう,面倒くさくないとも言えますが,でも,やっぱり後で何回も修正する可能性があ
るときは,いやなものです。特に,プログラミングしているときは,それぞれの定数とい
うのは,その意味が分かっているのだけれど,後になってみると,一体それはどういう意
味だったのかが分からなくなったりして,間違いのもとになることが多いので,できるだ
けその意味が分かるようにする必要があります。つまり,標語的に言うと,
「数値のまま残すのはトラブルのもと」
とでもいいましょうか。そこであらたしく「定義」するときだけにしたいものです。たと
えば,上のプログラムの場合は,
CLS
DIM x(100)
FOR i = 1 TO 100
x(i) = RND
NEXT
FOR i = 1 TO 100
PRINT x(i);
NEXT
PRINT
max = 0
FOR i = 1 TO 100
IF x(i) > max THEN max = x(i)
NEXT
PRINT "max = "; max
この下線部の部分は,それぞれ、「配列xの大きさ」という意味ですよね。そのため,2
00に変更するためには,これを全部修正する必要があったわけですが,意味が明確なの
ですから,そういう関数もありそうです。そう,あるんです。それは,
UBAOUND(x)
という関数です。UpperBOUND つまり上限という意味の関数です。これを使うは,次のよ
うなプログラムに修正することができます。
CLS
INPUT "Size of Data "; Num
DIM x(Num)
FOR i = 1 TO UBOUND(x)
x(i) = RND
NEXT
FOR i = 1 TO UBOUND(x)
PRINT x(i);
NEXT
PRINT
max = 0
FOR i = 1 TO UBOUND(x)
IF x(i) > max THEN max = x(i)
NEXT
PRINT "max = "; max
なお,このプログラムの場合には,Num という変数を使ってもいいんですけどね。
12−3 FUNCTIONを作ろう −引数の概念に注意−
さて,前回,FUNCTION文というのをやりましたね。上記のプログラムもFUNCTIONを使っ
てみる手もあるように思います。メインを次のようにしてみましょう。そして,これで稼
働するようにするには,FUNCTION文をどのように作ったらいいか考えてみてください。
CLS
INPUT "Size of Data "; Num
DIM x(Num)
FOR i = 1 TO UBOUND(x)
x(i) = RND
NEXT
FOR i = 1 TO UBOUND(x)
PRINT x(i);
NEXT
PRINT
max = GetMax(x()) ← x()という表記はミスではない。
PRINT "max = "; max 行列を渡すときには,こう表す。
作るべきFUNCTION文は,
FUNCTION GetMax(x())
.....
END FUNCTION
さて,ここで一つ注意しておきます。上記のプログラムでは,Num という変数を使っても
,UBOUND(x) を使っても良かったのですが,今回のFUNCTION文では,Num は使えません。
何故でしょう。それは,
メインとFUNCTIONは別のプログラム
ですから,
メインのNum とFUNCTIONのNum は別の変数
だからです。
両方は独立ではありますが,全く無関係では意味がないので,いくつかの変数は受渡しを
しています。具体的には,
メイン : max = GetMax(X())
FUNC : FUNCTION GetMax(x())
という記述の部分です。これによって,メインの方の変数 x()のデータをFUNCTIONの方の
データ x()として扱うよということです。
また,ここで注意する必要があるのは,変数の名前は一致させる必要はありません。名
前が違っていても,目的が同じならば,OKなのです。このような変数のことを,「引数」
といい,詳しくは,メインの方を実引数,FUNCの方を仮引数といいます。
例えば,次のようなプログラムはOKです。
CLS
INPUT "Size of Data "; Num
DIM y(Num)
FOR i = 1 TO UBOUND(x)
y(i) = RND
NEXT
FOR i = 1 TO UBOUND(x)
PRINT y(i);
NEXT
PRINT
max = GetMax(y()) ←−−+
PRINT "max = "; max | 名前が違う。
| メインのy() をFUNCの x()に代入するという
FUNCTION GetMax(x()) ←−−+ような気持ち。 (本当はちょっと違うけど)
.....
12−4 「やってること」が分かるようなメインがいいよね
さて,ところで,元のプログラムのメインは次のような感じに書けると分かりやすいで
すよね。
CLS
DIM x(100)
配列x() に乱数で値を定義せよ
配列x() を表示せよ
max = GetMax(x())
PRINT "max = "; max
このままでは無理ですが,次のように書くことはできます。
CLS
DIM x(100)
CALL SetRND(x())
CALL Show(x())
max = GetMax(x())
PRINT "max = "; max
そうか,そういう便利な SetRND とか Show っていうのがあるのかと思うかもしれませ
んが,そんなものは「ありません」。
そうじゃなくて,そういうのを「作る」んです。FUNCTION文のときと同じで,そういう
ものを作ることによって,プログラムがすっきりと見えるようになり,全体が構造的にな
っていくわけです。
12−5 SUB文
さて,どうやって「作る」か。それを SUB文といいます。具体的に,作った例から
話をする方がいいと思うので,そうしましょう。
SUB SetRND(x())
FOR i = 1 TO UBOUND(x)
x(i) = RND
NEXT
END SUB
こうすることによって, x()をもらったときの「お仕事」をSUB によって行うわけです。
さて,同様にして,Showを作ってみてください。
12−6 ソーティング
さて,今までは,「最大値」を求めることをしてきましたが,乱数でいろんなデータが
並んでいるという状況だったら,「整列」させてみたいですよね。そういうことを,「ソ
ーティング」と言います。
典型的なプログラムを以下に書きます。
DIM x(100)
FOR i = 1 TO 100
x(i) = RND
NEXT
FOR i = 1 TO 100
PRINT x(i);
NEXT
PRINT
FOR i = 1 TO 100
FOR j = 1 TO 100
IF i < j AND x(i) > x(j) THEN SWAP x(i), x(j)
NEXT
NEXT
FOR i = 1 TO 100
PRINT x(i);
NEXT
PRINT
ここでのSWAPというのは,「取り替える」という意味です。
さて,これを,メインが次のようになるようにしてみましょう。
CLS
DIM x(100)
CALL SetRND(x())
CALL Show(x())
CALL Sort(x())
PRINT
CALL Show(x())
12−7 ソーティングの結果を目で見る
数値で見てもつまらないですね。じゃあ,こうしましょう。もちろん,他のSUB も使い
ますが,以下では省略しています。
SCREEN 12
CLS
DIM x(100)
CALL SetRND(x())
CALL DispGraph(x(), 0, 200)
CALL Sort(x())
PRINT
CALL DispGraph(x(), 200, 200)
SUB DispGraph (x(), StartY, height)
ub = UBOUND(x)
dx = 640 / ub
FOR i = 1 TO ub
LINE ((i - 1) * dx, StartY)-(i * dx, StartY + x(i) * height), , B
NEXT
END SUB
12−8 ソーティングの様子を目で見る
どうせなら,その様子も見たいですよね。こう修正してみましょう。
SCREEN 0
CLS
DIM x(100)
CALL SetRND(x())
CALL DispGraph(x(), 0, 200)
CALL DispGraph(x(), 200, 200)
CALL Sort(x(), 200, 200)
PRINT
CALL DispGraph(x(), 200, 200)
SUB Sort (x(), StartY, height)
ub = UBOUND(x)
dx = 640 / ub
FOR i = 1 TO UBOUND(x)
FOR j = i TO UBOUND(x)
IF x(i) > x(j) THEN
LINE ((i - 1) * dx, StartY)-(i * dx, StartY + x(i) * height), 0, B
LINE ((j - 1) * dx, StartY)-(j * dx, StartY + x(j) * height), 0, B
SWAP x(i), x(j)
LINE ((i - 1) * dx, StartY)-(i * dx, StartY + x(i) * height), , B
LINE ((j - 1) * dx, StartY)-(j * dx, StartY + x(j) * height), , B
END IF
NEXT
NEXT
END SUB
12−9 もっと効果的なソーティングはないの(1)
つまらない修正のように見えますが,どんな効果があるでしょう。
SUB Sort (x(), StartY, height)
ub = UBOUND(x)
dx = 640 / ub
FOR i = 1 TO UBOUND(x)
FOR j = UBOUND(x) TO i STEP -1
IF x(i) > x(j) THEN
LINE ((i - 1) * dx, StartY)-(i * dx, StartY + x(i) * height), 0, B
LINE ((j - 1) * dx, StartY)-(j * dx, StartY + x(j) * height), 0, B
SWAP x(i), x(j)
LINE ((i - 1) * dx, StartY)-(i * dx, StartY + x(i) * height), , B
LINE ((j - 1) * dx, StartY)-(j * dx, StartY + x(j) * height), , B
END IF
NEXT
NEXT
END SUB
12−10 もっと効果的なソーティングはないの(2)
何をやっているんでしょうね。
SUB Sort (x(), StartY, height)
ub = UBOUND(x)
dx = 640 / ub
FOR i = 1 TO UBOUND(x)
Min = i
FOR j = i TO UBOUND(x)
IF x(Min) > x(j) THEN Min = j
NEXT
j = Min
LINE ((i - 1) * dx, StartY)-(i * dx, StartY + x(i) * height), 0, B
LINE ((j - 1) * dx, StartY)-(j * dx, StartY + x(j) * height), 0, B
SWAP x(i), x(j)
LINE ((i - 1) * dx, StartY)-(i * dx, StartY + x(i) * height), , B
LINE ((j - 1) * dx, StartY)-(j * dx, StartY + x(j) * height), , B
NEXT
END SUB
なお,ソーティングには,他にもいろいろな技法があります。ここでは,それを学習す
ることが目的ではないので,あまり触れませんが,関心がある人は調べてみてください。
アリゴリズムによって,こんなにも速度が変わるのかと感心するものがいろいろとあるは
ずですし,考えるといろいろな方法があるものだと感心すると思います。