同じ内容の操作を何度も行ったり,1つのことをするのに長いプログラムが必要な場合は,その内容をひとまとめにすることが出来れば,楽ですしバグの発生する可能性も下がります。
次の例を見てみましょう。
#prompt Dim T As String Dim i As Double, n As Double, v As Double, s As Double Print "平均値を求めます。" Do Print "データ件数"; Input T v=Val(T) Loop Until v>0 ' 件数が0以下の場合は入力し直し n=v s=0 For i=1 To n Do Print Str$(i)+"人目のデータ"; Input T v=Val(T) Loop Until v>0 ' データの値が0以下の場合は入力し直し s=s+v Next Print "平均値は"; s/n; "です。"
平均値を求めます。 データ件数? -4 データ件数? 3 1人目のデータ? 1.2 2人目のデータ? -3.0 2人目のデータ? 0 2人目のデータ? 1.8 3人目のデータ? 1.6 平均値は 1.53333333333333 です。
この場合,「データ件数を入力する部分」と「データの値を入力する部分」が共通ですね。(実際はもっと簡潔に書ける部分もあるのですが,少し直してあえて共通にしているのです(爆)。)
そういうところを1つにまとめるのが,今回のテーマである「プロシージャ」の働きなのです。
プロシージャとは,いくつかの処理を1つのブロックにまとめ,それを1つの操作として扱うためのものです。そのうち,単に処理のみを定義するものがSubプロシージャなのです。
下のプログラムは,上のものをSubプロシージャを使って書き直したものです。
#prompt
Dim T As String
Dim i As Double, n As Double, v As Double, s As Double
Print "平均値を求めます。"
InputNumber("データ件数")
n=v
s=0
For i=1 To n
InputNumber(Str$(i)+"人目のデータ")
s=s+v
Next
Print "平均値は"; s/n; "です。"
Sub InputNumber(Message As String)
Do
Print Message;
Input T
v=Val(T)
Loop Until v>0
End Sub
後ろに書いた「Sub InputNumber」がポイントになります。
Sub [プロシージャ名] 〜 End Subの部分でプロシージャが行う内容を定義します。
この際,プロシージャの後ろのカッコの中に変数の宣言が入っていますが,これは関数のときと同様プロシージャの引数と呼びます。
このとき注意すべきなのは,プロシージャの引数の変数名はプロシージャの内部だけで有効ということです。
例えば,以下のコードを見て下さい。
#prompt Dim T As String Dim i As Double, n As Double, v As Double, s As Double Print "平均値を求めます。" InputNumber("データ件数") ' InputNumber プロシージャの中の変数の値を取得しようとしているが・・・ Print "メッセージ:"; Message n=v s=0 For i=1 To n InputNumber(Str$(i)+"人目のデータ") s=s+v Next Print "平均値は"; s/n; "です。" Sub InputNumber(Message As String) Do Print Message; Input T v=Val(T) Loop Until v>0 End Sub
これをコンパイルしても,「"Message" 無効な識別子です」となって失敗します。つまりMessageという変数はInputNumberプロシージャの中でのみ有効なのです。
なお,同様にプロシージャの中でDim文を用いて変数を宣言すると,その変数はプロシージャの中だけで有効となります。
ですので,普通同じ名前の変数は重複して宣言できないのですが,下のプログラムは問題はありません。
#prompt Print1(10) Print2(5) Print3(4) Sub Print1(n As Long) ' 指定回数「1」を表示するプロシージャ Dim i As Long For i=1 To n Print 1; Next End Sub Sub Print2(n As Long) ' 指定回数「2」を表示するプロシージャ Dim i As Long For i=1 To n Print 2; Next End Sub Sub Print3(n As Long) ' 指定回数「3」を表示するプロシージャ Dim i As Long For i=1 To n Print 3; Next End Sub
(実行結果)
1111111111222223333
問23
(1)正の整数 n を指定すると,それに対して 1+2+・・・+ n の値を計算して表示するSubプロシージャを作成せよ。
(2)そのプロシージャを,nを1から10まで変化させて呼び出せ。(注:プロシージャの内容を実行することを「呼び出す」と言うことがある。)
上の2つの例では,引数はどちらも1つでしたが,0個,または2つ以上でもよいです。
プロシージャを定義する側も呼び出す側も,引数がない場合は空のかっこを付け,2つ以上を指定する際は変数を「,」で区切ります(以下の例を参照)。
' 九九の表 #prompt Dim i As Long, j As Long For i=1 To 9 For j=1 To 9 PrintDigit(i*j, 3) ' 引数が2つのプロシージャの呼び出し方 Next ToNextLine() ' 引数のないプロシージャの呼び出し方 Next ' 次の行に進むプロシージャ '(プロシージャ化するほどでもないのですが・・・説明のため) Sub ToNextLine() Print ' Print文で何も指定しないと,改行するのみ End Sub ' 桁数を揃えて表示するプロシージャ Sub PrintDigit(number As Long, digit As Long) Dim i As Long ' プロシージャの外と重複で「i」を宣言しているが大丈夫。(前述) i=Int(Log(number)/Log(10)+1) ' 桁数を求める(注) If i<digit Then Print String$(digit-i, " "); ' 足りない桁数を埋めるスペースを入れる End If Print number; ' 元の値を表示する End Sub
実行結果
1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81
(注)log10n は以下のように変化する。つまり桁数より1少ない値が出る。(logについては「指数関数の基本」を参照)
n | 1 | ・・・ | 10 | ・・・ | 100 | ・・・ | 1000 | ・・・ |
log10n | 0 | ・・・ | 1 | ・・・ | 2 | ・・・ | 3 | ・・・ |
問24
3つの数値を指定し,以下のように処理を行うSubプロシージャ「Compare_Three」を作成せよ。
(1)1番目の引数の値が,2番目のもの以上でかつ3番目のもの以上であるときは,「1番目が最も大きい」と表示する。
(2)2番目の引数の値が3番目のもの以上であるときは,「2番目が最も大きい」と表示する。
それ以外の場合は「3番目が最も大きい」と表示する。
#prompt Compare_Three(1, 5, 7) ' 「3番目が最も大きい」が表示される Compare_Three(6, 3, 2) ' 「1番目が最も大きい」が表示される Compare_Three(-1, 8, 6) ' 「2番目が最も大きい」が表示される Compare_Three(4, 4, 2) ' 「1番目が最も大きい」が表示される Compare_Three(7, 2, 7) ' 「1番目が最も大きい」が表示される Compare_Three(3, 9, 9) ' 「2番目が最も大きい」が表示される Compare_Three(8, 8, 8) ' 「1番目が最も大きい」が表示される ' 以下にCompare_Threeプロシージャの定義を書く(問題)