Lesson6 サブルーチン-SubプロシージャとFunctionプロシージャ-
今まで紹介してきたサンプルスクリプトは、基本的にどれも最初の行から最後の行まで一行ずつ実行して終了するものでした。しかし、この方法では、同じ内容の処理をさせたいときでも、同じコードを何回もスクリプト中に書かなければなりません。これではコードが長くなる上、見にくくなってしまいます。
そこでこのLesson6では、SubプロシージャおよびFunctionプロシージャを導入して、ある処理を一つのサブルーチンとし、メインルーチンから何回でも呼び出せるようにする方法について述べることにします。
まずSubプロシージャの例から見ていきましょう。以下は閏年判定スクリプトです。
ちなみに西暦で年度を表示したとき、それが4で割り切れる場合のうち、100で割り切れない年が閏年です。ただし、400で割り切れる年は閏年になります。
LeapYear 1900 Call LeapYear(1996) Call LeapYear(1997) Call LeapYear(2000) Call LeapYear(Year(Date)) 'LeapYearプロシージャ。引数としてintYear(西暦で表示した年度)を指定すると、その年が閏年か否かを表示します。 Sub LeapYear(intYear) If intYear mod 4<>0 Or (intYear mod 100=0 And intYear mod 400<>0) Then Msgbox intYear & "年は平年です。" Else Msgbox intYear & "年は閏年です。" End If End Sub
このスクリプトの説明にはいる前に、一般的なSubプロシージャの形式について述べておきましょう。
Subプロシージャは、
Sub ProcedureName(option)
.....
End Sub
という形式で記述します。optionは、このプロシージャに与える引数です。引数を指定すると、その引数をSubプロシージャ内で参照することができます。引数は , で区切って複数指定することもできます。
また、引数は省略することもできます。その場合は、
Sub ProcedureName()
のように、かっこをつけるのを忘れないでください。
Subプロシージャを呼び出すときは、メインルーチン (要はSubプロシージャ外の部分) から
ProcedureName value
のように、Subプロシージャ名に続いて何らかの値を指定すると、その値を読み込んでSubプロシージャに制御が移ります。この時、指定した値がSubプロシージャの引数optionに代入されることになります。なお、Subプロシージャで指定した引数の数と、呼び出すときに指定する引数の数は一致させておかなければなりません。
また、Subプロシージャを呼び出すには、次のような構文も利用できます。
Call ProcedureName(value)
このようにCallステートメントを使うときは、値にかっこをつけます。
Subプロシージャが終了した後は、またメインルーチンに制御が戻り、このSubプロシージャを呼び出した次の行から実行が再開されます。また、メインルーチンが終了すれば、もうSubプロシージャが呼び出されることはありません。
Subプロシージャ内でExit Subと記述すると、そこでSubプロシージャを抜けることができます。これはFor...Nextステートメントで登場した、Exit Forなどと同じような使い方をします。
さて、以上をふまえて上のスクリプトを見ていきましょう。このスクリプトでは、後半でLeapYearという名前のSubプロシージャを記述することで、「その年が閏年か平年か表示させる」ルーチンを何回も呼び出せるようになっています。
ごらんの通り、前半部分で年度を引数として、LeapYearを呼び出しています。Call(Year(Date))というのは、VBSのDate関数
(今日の日付を日付型で返す) とYear関数
(引数として日付型の値を与えると、そのうち「年」の部分だけを返す)
を使って、今年が閏年かどうかを判別する行です。
次にLeapYearプロシージャの中身を見てみましょう。
まず、LeapYearはintYearという引数を受け入れます。先ほどいろいろな年を引数として指定しましたが、それらの値がintYearに代入されるわけです。
このintYearが、閏年かどうかを判定するためにIf文を用いています。ここでいくつかの演算子が登場しています。ついでですので演算子について簡単にレクチャーしましょう。
演算子 | 演算子の種類 | 意味 | 他の例 (詳しくはVBSリファレンス参照) |
---|---|---|---|
mod | 算術演算子 | A mod B とすると、AをBで割ったときの余りを返す。 | +,-,*,/(わり算),\(わり算の商)など |
Or,And | 論理演算子 | A Or B … 条件Aもしくは条件Bのどちらか一方、あるいは両方が成り立つ場合(AとBの論理和
; A∪B ) A And B … 条件Aと条件Bの両方が成り立つ場合 (AとBの論理積 ; A∩B) |
Not,Xorなど |
<> | 比較演算子 | A <> B … A と B は等しくない | >= (以上) < (より小さい) = (等しい) など |
演算子というと難しく聞こえますが、慣れれば怖くありません。とにかくこの表をもとに、先ほどのIf文が何を意味しているか、読み解いていきましょう。
If intYear mod 4<>0 Or (intYear mod 100=0 And intYear mod 400<>0) Then
まずはかっこの中から見ましょう。ちなみに論理演算も、算術演算と同様に、かっこをつけた部分が優先されます。かっこの中の意味は、「intYearは100で割り切れ、かつ、400で割り切れない場合」となります。よって全体では、「intYearは4で割り切れない、もしくは、(100で割り切れ、かつ、400で割り切れない)場合」という意味になります。これは閏年ではない条件ですので、平年ということになります。
Elseは、「平年ではない場合」となるので、閏年の条件となります。
西暦2000年は、閏年なんですね。
さて、次にFunctionプロシージャの例を見ていきますか。これは、高さ・幅・奥行きを与えると体積を返すというスクリプトです。
MsgBox "一辺12cmの立方体の体積は" & Volume(12,12,12) & "cm^3です。" MsgBox "高さ50.8cm、幅60.6cm、奥行き45.3cmのテレビの体積は" & Volume(50.8,60.6,45.3) & "cm^3です。" Function Volume(sngHeight,sngWidth,sngDepth) Volume=sngHeight*sngWidth*sngDepth End Function
何でテレビか? という疑問はさておき、Volumeプロシージャの中身を見ていきましょう。Functionプロシージャは、Subプロシージャ同様、複数の引数を指定できます(あるいは引数なしも可)。この例では、VolumeプロシージャにsngHeight,sngWidth,sngDepthの3つの引数を指定しています。sngとは、単精度浮動小数点数型というデータ型の変数につける接頭語です。単精度浮動小数点数型とは、小数以下の値をもつ数字の型と考えて結構です。倍の精度を持つ倍精度浮動小数点数型(dbl)というのもあります。まあ、VBSでは変数宣言時に型の宣言ができないので、実際はどちらの型で処理されるかわからないのですけどね。(TypeName関数等で調べる方法はありますが)
Functionプロシージャが、Subプロシージャと異なる点は、Functionプロシージャは値を返すことです。すなわち、VolumeというFunctionプロシージャ内で、Volumeに値を代入させておくと、その値がVolumeの返値になるわけです。といってもわかりにくいので実例を見てください。Volumeプロシージャ内で、Volume=sngHeight*sngWidth*sngDepth とすることで、Volumeには高さ×幅×奥行き、すなわち体積が代入されます。よってVolumeは高さ、幅、奥行きの3つの引数を与えると、体積を返すという関数のような働きをするわけです。
なおFunctionプロシージャは、Function〜End Functionの間に記述し、途中でFunctionプロシージャを抜けるときは、Exit Functionを用います。
Functionプロシージャが値を返すという特性を用いて、先ほどの閏年判定スクリプトを書き換えてみましょう。これは、ユーザーが入力した任意の年が閏年かどうか判定するスクリプトです。終了するときは「キャンセル」ボタンを押しましょう。
Dim intNumber Do intNumber=InputBox("4桁の数字を入れてください。","閏年判定",Year(Date)) If intNumber="" Then Wscript.Quit If blnLeapYear(intNumber) Then MsgBox intNumber & "は、閏年です。" Else MsgBox intNumber & "は、平年です。" End If Loop Function blnLeapYear(intYear) '引数として年度を与えると、それが閏年ならTrue、平年ならFalseを返す。 If intYear mod 4<>0 Or (intYear mod 100=0 And intYear mod 400<>0) Then blnLeapYear=False Else blnLeapYear=True End If End Function
まず、InputBox関数でユーザーに任意の4桁の数を入力させています。InputBox関数の第三引数は、テキストフィールドに表示させる、既定の文字列です。ここでは、Year(Date)とすることで、今年の年度を既定の文字列としています。
InputBoxでキャンセルを押すか何も入力せずにOKボタンを押すと、InputBoxは長さ0の文字列""を返します。そこでIf文を使って、intNumberが長さ0の文字列の場合は、スクリプトを終了させるようにしています。スクリプトを終了させるには、WscriptオブジェクトのQuitメソッドを用います。ただし、このスクリプトの場合は、Do文を脱出するとスクリプトも終了する
(Loopの後に行がないため) ので、Wscript.QuitのかわりにExit
Doを使ってもよいでしょう。
(ちなみにキャンセルボタンを押すときは、厳密にはただの空の文字列ではなく、Empty値という特殊な値を返す。ValType関数かIsEmpty関数で、キャンセルボタンを押したか、何も入力せずにOKボタンを押したか区別できる。Empty値、ValType関数、IsEmpty関数についてはVBSリファレンスをどうぞ)
次に、blnLeapYearというFunctionプロシージャを用いて、intNumberが閏年か否かを表示させています。閏年ならblnLeapYearはTrueという値を、平年ならFalseという値を返します。True、Falseというのは、ブール型の値で、Trueは真、Falseは偽という意味になります。ブール型は、TrueかFalseのどちらかの値しか持たない型です。この例のように、ある命題が、「正しい」か「正しくない」かの2つの解しか持ち得ない場合、ブール型を用いると便利です。
If blnLeapYear(intNumber) Then というのは、「IfとThenの間がTrueの時」という意味ですから、If blnLeapYear(intNumber)=True Then と書いても同じです。同じように、If A=10 Then というのは、If (A=10)=True Then という意味です。A=10の=は、比較演算子の一つで、このようにブール型の結果を返します。If文というのはブール型の値を判別する式であるわけです。
最後に、SubプロシージャとFunctionプロシージャを同時に使った例を挙げます。レートが変動すると円とドルの価値がどう変わるか、シミュレートします。(ってそんなたいそうなもんでもないが)
Call ShowRate(79.5) Call ShowRate(100) Call ShowRate(119.01) Call ShowRate(150) Sub ShowRate(sngRt) MsgBox "1ドル=" & sngRt & "円の時、10ドルは" & Yen(10,sngRt) & "円、1000円は" & Dollar(1000,sngRt) & "ドルになります。" MsgBox "1ドル=" & sngRt & "円の時、100ドルは" & Yen(100,sngRt) & "円、10000円は" & Dollar(10000,sngRt) & "ドルになります。" End Sub Function Yen(sngDollar,sngRate) Yen=Round(sngDollar*sngRate,2) End Function Function Dollar(sngYen,sngRate) Dollar=Round(sngYen/sngRate,2) End Function
[注意]
Round関数は、指定した数を、指定した小数点で丸める(切り捨てる)関数です。たとえばRound(3.14159,2)は3.14を返します。
またShowRateの引数sngRtは、レートを指定するものです。YenとDollarはsngRate(レート)とドルあるいは円の値を与えると、円あるいはドルの値を返すものです。なお、レートを表すのにsngRtとsngRateの2つの変数を使っているのは、SubプロシージャとFunctionプロシージャで変数が衝突しないように分けるためです。
Lesson6ではSubプロシージャとFunctionプロシージャの使い方(+いろいろ)を述べました。次回は、エラー処理に関する話です。