1.2 VBAを使ってみよう

VBAとマクロの違いやプログラミング言語としてのVBAについて説明します。


1.2.1 マクロとVBAの違い

記録したマクロはEXCELの命令を並べてだけなので、常に同じ操作しかできません。プログラミング言語の一種であるVBAでは、変数を使ったり、処理の流れを条件によって変えることができます。この2つを組み合わせることによって複雑な処理を実行することも可能になります。また、Windowsの機能を呼び出すことによって、EXCELの命令だけではできないことも実現できます。
図1.2.1-1図1.2.1-1 条件に応じて処理の流れを変える

1.2.2 処理の流れを変えてみよう

変数を用いて処理の流れを変えてみましょう。第1章で作成したMacro2()を以下のように編集します。「'」の後はコメント(注釈)になり、プログラムの実行には関係ありません。プログラムを分かりやすくするためにできるだけ詳しく書くようにします。
図1.2.2-1図1.2.2-1

この処理を流れ図で表すと以下のようになります。プログラムと流れ図を見比べて、プログラムのどのステートメント(文)が流れ図のどの処理に相当するのか把握しましょう。
図1.2.2-2図1.2.2-2

プログラムで変更した部分について簡単に解説してみます。

宣言文

    Dim c_ix As Long    ' index for column
    Dim r_ix As Long    ' index for row
    Dim iro             ' 塗りつぶしの色
この3行は、変数の宣言です。直接、処理に関係しないので、流れ図には表現されていません。あらかじめ使う変数を明示するために書きます。宣言文など書かなくても良いというのが Basicのいいところでもあるのですが、プログラムが長くなると、どこでどんな変数を使っているのか明記しておかないと、後で把握するのが難しく、ちょっとした変更で思いもしない動きをしてしまうことがあります。保守性や可読性をよくするために、宣言文を省けないように、マクロを記録した時に、
Option Explicit
というステートメントが冒頭に自動で付けられていますので、宣言を省略すると実行時にエラーがでます。ちょっとしたプログラムで、後でメンテナンスの必要がない場合は面倒です。必要に応じてコメントアウトしておくと良いでしょう。

変数の初期値

    c_ix = 4            ' 列の指標に4を代入(D列を示す)
    r_ix = c_ix         ' 行の指標に4を代入
この2行は、変数に値を代入するステートメントです。変数c_ix には、定数4を代入しています。r_ixには変数c_ixの値を代入しています。その結果、2つの変数の値は等しく4です。
変数の初期化は、できるだけ使う直前で行うのが理想的です。

    If c_ix Mod 2 = 0 Then
        iro = 65535     ' 黄色
    Else
        iro = 5296274   ' 緑色
    End If
ここで処理の流れを条件によって2つに分けています。IfとThenで挟まれた「c_ix Mod 2 = 0」が条件式です。「c_ix Mod 2」は、除算の余りを求める剰余演算式です。c_ixを2で割った余りですから、0か1の値をとります。後半の「= 0」で0に等しいかどうか判定しています。0に等しい場合は「Then」に続くいくつかのステートメントを実行し、そうでない場合は「Else]に続くいくつかのステートメントを実行します。すなわち、変数c_ixの値が偶数の場合は、変数iroに黄色の値(65535)を設定し、奇数の場合は、緑の値(5296274)を設定しています。
図1.2.2-3図1.2.2-3「If...Then...Else 」の構文

条件式の中で使った「Mod」や「=」を演算子と呼びます。演算子には他に「+(加算)」「-(減算)」「*(乗算)」「/(除算)」などもあります。「=」は変数に値を代入する代入演算子としても使われ、ここでは値が等しいか判断するために比較演算子として使われています。文脈によって働きが決まっています。

また、演算子には優先順位が決められていて、優先順位の高い方から実行されます。「Mod」の方が「=」より優先順位が高いので先に評価(計算)されます。「+(加算)」「-(減算)」よりも「*(乗算)」「/(除算)」の方が優先順位が高いので先に評価されます。優先順位が同じものは左から、あるいは「( )」カッコで囲まれた式から評価されます。このように演算を評価する規則は算数とだいたい同じなので分かりやすいでしょう。

算術演算子、比較演算子、論理演算子等のキーワードで「Visual Vasic Editor」のヘルプを検索してみましょう。

    Cells(r_ix, c_ix).Select    ' セルD4を選択
変数r_ixとc_ixの値を使ってセルを選択しています。両方とも4が入っているのでセル"D4"を指定することになります。
        .Color = iro
「If...Then...Else 」を使って設定した変数iroの値で、選択したセルを塗りつぶします。

プログラムの処理が理解できたら実際に動かしてみましょう。Excelシートのウィンドウを最大化しておき、Visual Basic Editorのウインドウを小さめにして実行すると分かりやすいでしょう。試しにc_ixの値を1から1つずつ増やして実行してみてください。Macro2の中に文字カーソルを置いておき、実行ボタンをクリックすると簡単にMacro2を実行できます。
図1.2.2-4図1.2.2-4 実行例

1.2.3 処理を繰り返してみよう

2.2では変数の値が偶数か奇数かによって処理の流れを変えてみました。動作を確認するたびに変数の値を編集しましたが、これを自動的に1から7まで1つずつ増やして繰り返す処理を書いてみましょう。

繰り返し(ループ)という処理は、いろいろなパターンがあるので、VBAでもいくつかステートメントが用意されていますが、ここでは「For...Next」を使ってみましょう。「For...Next」についての詳しい説明は、「Visual Vasic Editor」の「ヘルプ」メニューから「Visual Basic ランゲージ リファレンス」→「ステートメント」→「A-L」と辿ってゆくと見つかります。

繰り返しを記述するためのステートメントは、他に「Do...Loop」、「While...Wend」、「For Each...Next」などがあります。どれを使っても繰り返し処理が記述できます。どのような違いがあるか調べておきましょう。また、2.1で説明した「If...Then...Else」についても調べてみましょう。
図1.2.3-1図1.2.3-1

さて、ヘルプで記述されている「For...Next」の構文を書きなおしてみました。「For...Next」ステートメントは、counterの値を、startから始め、いくつかのステートメント(statements)を実行する毎に、stepずつ増やし、endになるまで繰り返し実行するステートメントです。[ ] の部分は省略可能です。

構文

For counter = start To end [Step step]

    [statements]

Next [counter]
流れ図で表すと図1.2.3-2のようになります。構文の[ ]でくくられた部分は省略できるという意味です。[Step 値4]を省略した時の値は1です。値のところには変数や式も使えます。同じく、Nextの後の[変数1]も省略できます。繰り返しを二重三重に入れ子にした時、それぞれのループの範囲を分かりやすくするために使います。
図1.2.3-2図1.2.3-2

ここで、Macro2()を自動的に繰り返すように改修してみました。

Sub Macro2()
'
' Macro2 Macro
'
    Dim c_ix As Long    ' index for column
    Dim r_ix As Long    ' index for row
    Dim iro             ' 塗りつぶしの色

'   ### 繰り返しのための変数と定数の宣言
    Dim i_counter As Integer    ' ### ループカウンタ
    Const C_start = 3           ' ### ループカウンタの初期値(定数)
    Const C_end = 7             ' ### ループカウンタの終了値(定数)

'   ### 3行目を指定するための定数
    Const C_rno = 3              ' ### 塗りつぶす行番号
'
    For i_counter = C_start To C_end    ' ### ループの開始
        c_ix = i_counter        ' ### 列の指標にi_counterを代入
        r_ix = r_rno            ' ### 行の指標にr_no(定数)を代入
        
        If c_ix Mod 2 = 0 Then
            iro = 65535     ' 黄色
        Else
            iro = 5296274   ' 緑色
        End If
        
        Cells(r_ix, c_ix).Select    ' セルD4を選択
        With Selection.Interior
            .Pattern = xlSolid
            .PatternColorIndex = xlAutomatic
            .Color = iro
            .TintAndShade = 0
            .PatternTintAndShade = 0
        End With
    Next                ' ### ループの終わり
End Sub

変数と定数の宣言および初期化

'   ### 繰り返しのための変数と定数の宣言
    Dim i_counter As Integer    ' ### ループカウンタ
    Const C_start = 3           ' ### ループカウンタの初期値(定数)
    Const C_end = 7             ' ### ループカウンタの終了値(定数)

'   ### 3行目を指定するための定数
    Const C_rno = 3             ' ### 塗りつぶす行番号
ループの回数を数えるためのカウンタ(i_counter)を宣言しています。カウンタの初期値(C_start)と終了値(C_end)は定数、また、塗りつぶす行を指定する行番号(C_rno)も定数です。定数の値は宣言した時に決めた値から変更できません。ここでは、定数であることが分かるように名前を「C(大文字のC)」から始めるようにしました。定数はステートメントの中に直接書くこともできるのですが、どのような意味をもった値なのか分かるように名前をつけています。こうしておくと、ある定数を長いプログラムのあちこちで使っている時、分かりやすいだけでなく、改修の際には、宣言文を変更するだけでもれなく変更できます。

ループ

    For i_counter = C_start To C_end    ' ### ループの開始
        c_ix = i_counter        ' ### 列の指標にi_counterを代入
        r_ix = r_rno            ' ### 行の指標にC_rno(定数)を代入

        ……セルの塗りつぶし処理……

    Next                ' ### ループの終わり
ループカウンタ(i_counter)の値を初期値(C_start)から始めて、1つずつ増やしながら「Next」までのステートメントを実行します。ループカウンタの値は「Next」に到達したときに1つ増やされ、処理は「For」に戻ります。ここで、ループカウンタの値を調べ、終了値(C_end)を越えていなければ、再び「Next」までのステートメントを繰り返し実行しますが、終了値を越えていたらループを抜け、「Next」の次のステートメントを実行します。
ループの中では、塗りつぶすセルを指定する列の指標(c_ix)にループカウンタの値を、行の指標(r_ix)に定数(C_rno)を代入して塗りつぶし処理を実行しています。

以上を流れ図で書いてみると図1.2.3-3のように書けます。細かいところは省略しています。プログラムと見比べて、処理の大まかな流れを把握してください。
図1.2.3-3図1.2.3-3

1.2.4 プログラムの動きを観察しよう

プログラムで繰り返し処理を書けるようになると、数行のステートメントを記述するだけでたくさんの作業を自動的にできるようになります。しかし、ひとつ間違えると無限ループという怖い落とし穴が待っています。

もし、無限ループに陥ってしまったときは、「ESC(エスケープ)」キーを押してみてください。
図1.2.4-1図1.2.4-1
図1.2.4-1のようにダイアログが表れます。「終了」をクリックすると処理を終われます。「デバッグ」を押すと「中断モード」に移行して、実行中の状態を確認することができます。

今回の「For...Next」で無限ループを偶然作ってしまうことは、あまりないと思いますので、図1.2.4-1のようなダイアログボックスを出して「デバッグ」するのは難しいのですが、「ステップイン」を使うと処理の「中断モード」に移行できます。更に「ステップイン」を選ぶか、「F8」キーを押すと1ステップずつ実行できます。宣言文などは、実行を伴わないので飛ばされます。
図1.2.4-2図1.2.4-2

中断モードでは、マウスカーソルを変数の上に乗せると、変数の値が確認できます。また、「ローカルウィンドウ」にも変数の値を確認することができます。「ローカルウィンドウ」が見当たらないときは「表示」メニューから「ローカルウィンドウ」をクリックして表示できます。
図1.2.4-3図1.2.4-3 ローカルウィンドウ

「ブレークポイント」を設定しておくと、実行ボタンをクリックして、そこまで一気に実行できます。「デバッグ」→「ブレークポイントの設定/解除」を選ぶか、設定したいステートメントの左側枠上をクリックします。 図1.2.4-4 ブレークポイントの設定解除
図1.2.4-4 ブレークポイントの設定解除

デバッグのためのツールは、メニューをみると他にもいろいろあります。「ステップオーバー」「ステップアウト」は、プロシージャ(関数のこと)を使っている場合に威力を発揮します。プロシージャについては、後で説明する予定です。

デバッグツールに頼りすぎると時間がかかるばかりではなく、大局を見落とすことにもなりかねません。設計や机上のデバッグも大事にしましょう。しかし、使い方を知っておくことは大事です。プログラミング環境に応じて用意されていますので、必要に応じてヘルプで調べるようにしましょう。

プログラムの流れが把握できたら、実際に動かしてみましょう。初期値や終了値などを変更して試してみましょう。デバッグツールの使い方も試してみましょう。

1.2.5 配列を使ってみよう

2.3では変数の値を1つずつ増やして繰り返す処理を考えてみました。では、規則的に増えて行かない場合はどのように書けばよいでしょう。例えば、5行目の2列目、3列目、5列目、7列目、11列目、13列目、17列目、19列目、23列目、29列目、31列目を黄色で塗る場合を考えてみましょう。

このような場合は配列を使うと便利です。配列は、図1.2.5-1のように要素と呼ぶ同じ型のデータを順番にならべたデータ構造です。添え字で何番目の要素か指定して参照します。
図1.2.5-1図1.2.5-1
上図のような配列a_sosuは以下のように領域を宣言して確保します。宣言すると0から10まで、11個の要素が確保されます(1から10に設定変更することもできます)。

    Dim a_sosu(10)  ' 配列の宣言
配列の初期値を代入するには以下のように代入文を使います。
    a_sosu(0) = 2
    a_sosu(1) = 3
    a_sosu(2) = 5
          ……
    a_sosu(10) = 31

配列の要素の値を参照するには以下のようにします。
    MsgBox a_sosu(0)
          ……
    MsgBox a_sosu(10)

しかし、これでは、要素数が増えると初期化が大変なので、以下のように初期化を書き換えることもできます。

Sub ary_test()
    Dim soeji       ' 添え字
    Dim a_sosu      ' 配列を入れる変数(実はポインタ)
    
    ' 変数に初期配列を代入(この場合は添え字は必ず0からになります)
    a_sosu = Array(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31)
    
    For soeji = 0 To 10
        MsgBox a_sosu(soeji)
    Next soeji
End Sub
まず、配列を入れる変数を確保します。データ型は、Variant型でなければ代入できません。
    Dim a_sosu as Variant     ' 配列を入れる変数
次に、Array関数で要素の並びをまとめて配列を作り、変数a_sosuに代入しています。
    a_sosu = Array(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31)
参照の方法は同じです。変数soejiを0から10まで1つずつ増やしてすべての要素を表示しています。
    Dim soeji       ' 添え字
    
    For soeji = 0 To 10
        MsgBox a_sosu(soeji)
    Next soeji

では、2.3と2.4で作ったMacro2()を改修して、5行目の2列目、3列目、5列目、7列目、11列目、13列目、17列目、19列目、23列目、29列目、31列目を黄色で塗る処理を作ってみましょう。以下のプログラムの空欄_[1]_、_[2]_、_[3]_を適当な変数名で埋め、プログラムを完成してください。

Sub Macro2_5()
'
' Macro2_5 Macro
'
    Const C_rno = 5     ' 塗りつぶす行は5行目
    Const iro = 65535   ' 塗りつぶしの色は黄色
    
    Dim C_start     ' 添え字の初期値(定数)
    Dim C_end       ' 添え字の終了値(定数)
    Dim a_sosu      ' 配列を入れる変数
    _[1]_ = Array(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31)
'
    Dim i_soeji As Integer    ' 配列の添え字
    C_start = LBound(a_sosu)  ' 添え字の最小値を設定
    _[2]_ = UBound(a_sosu)    ' 添え字の最大値を設定
'
    For i_soeji = C_start To C_end
        Cells(C_rno, a_sosu(_[3]_)).Interior.Color = iro
    Next
End Sub
ここで、LBound()とUBound()という2つの関数は、それぞれ与えられた配列の添え字が取りうる値の最小値と最大値を返す関数です。定数として持つことも可能ですが、このように関数で取出すように作っておくと、配列の要素数が変わった時にも対応できます。

塗りつぶしは、With ~ End With を使わずに1行ですませました。