連載
» 2007年01月12日 00時00分 公開

基礎解説 演習方式で身につけるチェック式WSH超入門:第8回 Functionプロシージャでユーザー独自の関数を定義する (2/3)

[牟田口大介(Microsoft MVP Visual Developer - Scripting),著]

引数を持つFunctionプロシージャ

 FunctionプロシージャもSubプロシージャと同様、引数を複数持つことができる。次の例は、引数を2つ取る例で、文字列と数値を指定することで、文字列をランダムな回数繰り返した文字列を返す。繰り返す回数は2つ目の引数である数値の値を大きくすることで相対的に多くなる。

Option Explicit
Randomize

MsgBox "あ" & Repeat("た",200) & vbCrLf & _
       "あ" & Repeat("た",10) & vbCrLf & _
       Repeat("無駄",10) & "ァ!" & vbCrLf & _
       Repeat("オラ",10) & "ァ!"

'*********************************************************
'用途: 入力した文字列を複数回繰り返した文字列を返す
'受け取る値: strWord: 繰り返す文字列(String)
'            intLength: この値が大きいほど文字列が長くなり
'                       やすくなる(Integer)
'戻り値: 複数回繰り返した文字列(String)
'*********************************************************
Function Repeat(strWord, intLength)
    Dim strRepeat
    strRepeat = ""
    '以下のステートメントを繰り返す
    Do
        strRepeat = strRepeat & strWord
        '1/intLengthの確率でループを抜ける(strRepeatの文字数が100を超えた場合も)
    Loop Until 1 = Int((intLength - 1 + 1) * Rnd + 1) Or Len(strRepeat) > 100
    '戻り値を返す
    Repeat = strRepeat
End Function

 このスクリプトを実行すると、例えば次のような結果が表示される。

Functionプロシージャ、Repeatの呼び出し例
指定した文字列をランダム回数繰り返した文字列が返され、それを表示している。

 FunctionプロシージャでもSubプロシージャと同様に複数の引数を与えることができるのがお分かりいただけると思う。ただし、その場合でも戻り値は常に1つであることに注意していただきたい。また、実引数と仮引数の数を一致させる点にも注意だ。

ループを実現するDoステートメント

 上のコード例では新しい構文、Do〜Loopステートメントを用いている。Do〜LoopステートメントはFor〜Nextステートメントと同様、繰り返しのための構文であり、Loop Untilの後の部分に指定した条件が真になるまで、DoとLoopの間に挟まれたステートメントが繰り返される。ここでは、Rnd関数が生成するランダムな数値(その範囲はintLength引数が決定する)と1が等しいという条件が真になる場合(この条件が真になる確率は1/intLengthである)、もしくはstrRepeatの文字数が100を超えた場合、ループを終了することになる。なおUntilを省略すると無限ループになってしまう。そのためIfステートメントを併用し、コード中で、ある条件になった時点でループを脱出するように、Exitステートメント(Exit Do)を記述しなければならない。例えば次のように記述する。

Do
    strRepeat = strRepeat & strWord
    '1/intLengthの確率でループを抜ける
    '(strRepeatの文字数が100を超えた場合も)
    If  1 = Int((intLength - 1 + 1) * Rnd + 1) _
      Or Len(strRepeat) > 100 Then
        Exit Do 'ループを抜ける
    End If
Loop

 また、Untilの代わりにWhileを用いると、「指定した条件が真の間」ループが繰り返される。UntilとWhileはDoの後に置くこともでき、その場合はループに入る前に条件が判定される。なお、Do〜Loopステートメントもネストが可能である。まとめると次のようになる。

条件の評価 条件が真になるまで 条件が真の間
ループの前 Do Until <条件>
  ステートメント…
Loop
Do While <条件>
  ステートメント…
Loop
ループの後 Do
  ステートメント…
Loop Until <条件>
Do
  ステートメント…
Loop While <条件>
Do〜Loopステートメントの種類
条件をループに入る前と後のどちらで評価するか、条件が真になるまで/真の間ループするかで使い分ける。「Until <条件>」や「While <条件>」を省略すると無限ループになる。ループの途中で抜け出したい場合はExitステートメント(Exit Do)を使用する。

 Do〜Loopステートメントに似た構文として、While〜Wendステートメントというものもあるが、これはもっと単純なループを記述するために用いる構文である。While〜Wendステートメントは次のように記述する。

While <条件>
  ステートメント…
Wend

 詳細は以下のヘルプなどをご覧いただきたい。

自分自身を呼び出す再帰呼び出し

 再帰呼び出しとは、プロシージャの中から自分自身のプロシージャを呼び出すことである。例として階乗(1からある数値までを掛け合わせた値。factorial)を求めるスクリプトを考える。階乗とは、「0! = 1、n>0 のとき n! = 1×2×3×…×n」と定義されるが、「0! = 1、n>0 のとき n! = (n-1)!×n」と書き換えることができる。例えば「5! = 4! × 5」と表現できる。このような、記号の定義の中に自分自身の記号が含まれているような場合に再帰呼び出しが有効である。

Option Explicit

MsgBox "0!=" & Factorial(0) & vbCrLf & _
       "1!=" & Factorial(1) & vbCrLf & _
       "2!=" & Factorial(2) & vbCrLf & _
       "3!=" & Factorial(3) & vbCrLf & _
       "5!=" & Factorial(5) & vbCrLf & _
       "10!=" & Factorial(10)

'*********************************************************
'用途: 与えられた自然数の階乗を返す
'受け取る値: intNumber: 階乗を求める自然数(Integer)
'戻り値: 与えられた数値の階乗(数値型)
'*********************************************************
Function Factorial(intNumber)
    If intNumber = 0 Then
        'intNumberが0のときは1を返して終了
        Factorial = 1
    Else
        '再帰呼び出し
        Factorial = Factorial(intNumber - 1) * intNumber
    End If
End Function

 このスクリプトを実行すると、次のように表示される。

FunctionプロシージャFactorialの実行例
階乗を求めるFunctionプロシージャFactorialを再帰的に呼び出している。

 このように、与えられた数値の階乗を求めることができる。このコード中でFunctionプロシージャFactorialを再帰的に呼び出している。例えば、引数に1を与えた場合(Factorial(1)の場合)どういう動きをするのか追ってみよう。まず、IfステートメントはintNumberが0でないのでElse以下が実行される。ここではintNumber - 1 = 1 - 1 = 0を引数として、Function プロシージャFactorialを再帰呼び出ししている(Factorial(0)が呼ばれる)。今度はintNumber = 0なので、Factorial(0)は1を返して終了する。ここでFactorial(0)は1を返したのでFactorial(1)に制御が戻り、Factorial(0)(=1)とintNumber(=1)の積が計算される。その結果、Factorial(1)は1 × 1 = 1と求まり、スクリプト・レベルに値を戻す。以上がFactorial(1)を実行した場合の制御の動きである。2や3を代入した場合も同様に再帰呼び出しの回数が増えるだけで基本は同じである。

 なお、再帰呼び出しはSubプロシージャにおいても可能である。今後、フォルダの下位フォルダを再帰的に検索するなどの例で再登場する予定なので覚えておいてもらいたい。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。