ES000001_VBAエキスパート(ExcelVBAスタンダード)合格講座/第一回:プロシージャと配列

VBAエキスパート(ExcelVBAスタンダード)合格講座/第一回:プロシージャと配列 ExcelVBA Standard
  1. このページの内容について
  2. プロシージャの呼び出し
    1. ベーシックまでの内容
      1. サンプルコード(クリックでコピー)
      2. 実行結果
    2. 値渡しと参照渡し
      1. 値渡し
      2. 参照渡し
      3. サンプルコード(クリックでコピー)
      4. 実行結果
  3. 配列
    1. 変数と配列の違い
      1. 変数のイメージ
      2. 配列のイメージ
    2. 要素とインデックス(添え字)
    3. 配列の種類(宣言方法による違い)
      1. 静的配列
        1. 宣言の方法
        2. サンプルコード(クリックでコピー)
        3. 実行結果
      2. 動的配列
        1. 宣言の方法
        2. サンプルコード(クリックでコピー)
        3. 実行結果
      3. ReDim(動的配列の再定義)
        1. サンプルコード(クリックでコピー)
        2. 実行結果
      4. ReDimで再定義済の配列を再定義する
        1. サンプルコード(クリックでコピー)
        2. 実行結果
      5. 入力済のデータを保持してReDimする
        1. サンプルコード(クリックでコピー)
        2. 実行結果
    4. 配列の種類(次元数による違い)
      1. いろいろな次元の配列
        1. サンプルコード(クリックでコピー)
        2. 実行結果
      2. 多次元配列をReDim(Preserve)するときの注意点
        1. サンプルコード(クリックでコピー)
        2. 実行結果
    5. 配列の要素数を取得する
      1. Lbound関数とUbound巻子
        1. サンプルコード(クリックでコピー)
        2. 実行結果
      2. 配列の要素に関する注意点
        1. サンプルコード(クリックでコピー)
        2. 実行結果
    6. Split関数
        1. サンプルコード(クリックでコピー)
        2. 実行結果
  4. 【PR】VBAエキスパート試験対策記事

このページの内容について

このページは、VBAエキスパート(ExcelVBAスタンダード)試験合格講座の第一回記事です。公式テキスト第1章「プロシージャ」と第2章「変数」の内容について解説します。自分でも手を動かしながら読み進めてください。1時間の講義でお話する程度の分量です。主に講座受講者の復習での利用を想定しています。

プロシージャの呼び出し

ベーシックまでの内容

プロシージャはマクロの最小実行単位です。プロシージャには値を返すことができないSubプロシージャと値を返すことができるFunctionプロシージャがあります。これだけ見ると値を返すことができるFunctionプロシージャのみを使えば良いように見えますが、呼び出し方やユーザーからの見え方に違いがあり、開発においてはそれぞれ使い分けて使用します。プロシージャはプロシージャを呼び出すことができます。

サンプルコード(クリックでコピー)

Private Sub Caller()

Call Msgbox(Adder(10, 11))

End Sub
Private Function Adder(ByVal x As Long, ByVal y As Long) As Long

Adder = x + y

End Function

実行結果

上記サンプルコード上段の「Caller」を実行すると、Functionプロシージャである「Adder」を呼び出します。「Adder」は2つの整数(Long)を受け取り、足し算した結果を返します。「Caller」は返された整数値「21」をそのままMsgbox関数に渡してメッセージを表示します。

値渡しと参照渡し

プロシージャは値を受け取ることができます。値の受け取り方には2種類あります。

値渡し

引数に「ByVal」を設定すると、値渡しになります。値渡しされた引数はプロシージャ内で書き換えをしても呼び出し元に影響しません。

参照渡し

引数に「ByRef」を設定すると、参照渡しになります。参照渡しされた引数はプロシージャ内で書き換えた内容が、呼び出し元に反映します。

「ByVal」「ByRef」を指定しない場合、「ByRef」が指定されたものとみなされます。

サンプルコード(クリックでコピー)

Private Sub RefCaller()

Dim a As Long, b As Long
a = 10
b = 11
Call ReferenceTest(a, b)
Debug.Print a
Debug.Print b

End Sub
Private Sub ReferenceTest(ByRef x As Long, ByVal y As Long)

x = x + 10
y = y + 10

End Function

実行結果

上記サンプルコード上段の「RefCaller」を実行すると「ReferenceTest」を呼び出します。「ReferenceTest」は第一引数を参照渡しで受け取り、第二引数を値渡しで受け取ります。そして2つの整数にそれぞれ「10」を足して処理を終了します。「RefCaller」は「ReferenceTest」に渡した2つの変数をそれぞれイミディエイトウィンドウに出力します。第一引数として渡した変数「a」には10が加算されており、第二引数として渡した変数「b」には呼び出し先の変更が反映されていないことを確認できます。

値渡しと参照渡しの違い・・長くなるので読み飛ばしOK。

PCは5大要素から構成されています。
 ①制御装置
 ②演算装置
 ③記憶装置
 ④入力装置
 ⑤出力装置
このうち、①と②の機能を持つのがCPU、③の機能を持つのがメモリです。マクロなどプログラムの実行時に変数などに格納されたデータはメモリ上に存在しています。そしてメモリは番地で管理されています。例えば、1番地~16番地までを使って変数xの値を格納している・・といった具合です。ここで値渡しは、この格納されたデータのコピーを作成して引数として渡します。参照渡しはこの番地を引数として渡します。その違いのために、値渡しでは、呼び出し先での変数の書き換えは呼び出し元に影響せず、参照渡しでは、呼び出し先での変数の書き換えが呼び出し元に影響するのです。

配列

変数と配列の違い

これまで学習してきた「変数」には一つのデータのみを格納することができました。このため、複数のデータを扱う場合、変数それぞれに名前つけてデータの数だけ変数を用意する必要がありました。配列は同じ型の複数の変数を連ねて一つの名前で管理することができるものです(下図)。

変数のイメージ

データひとつにつき1つの変数が必要であり、変数名の管理が大変・・。

配列のイメージ

1つの名前と番地(インデックス)で管理できる。表のように扱うことができる。

要素とインデックス(添え字)

配列は変数と同様、任意の名前を付けて宣言することで使用できます。配列に入っているデータひとつひとつを「要素」、データにアクセスするための数値を「インデックス」もしくは「添え字」と呼びます。

配列の種類(宣言方法による違い)

宣言の方法の違いで2種類の配列を使い分けるができます。

静的配列

宣言時に配列の大きさを指定し、マクロの実行中に配列の要素数を変更できない配列を静的配列(せいてきはいれつ)と呼びます。

宣言の方法

基本は変数と同じで、要素数は、変数名の後ろに「1 to 5」のように指定します。

サンプルコード(クリックでコピー)
Private Sub dim_static_arr()

Dim arr_1(0 to 2, 0 to 3) As String
Dim arr_2(1 to 9, 1 to 2) As Long
Stop

End Sub
実行結果

上記サンプルコードを実行すると「Stop」で動作が停止するため、ローカルウィンドウで中身を見てみましょう。「arr_1」と「arr_2」いう静的配列を宣言しています。

配列の始まりの添え字は0以上の任意の整数で指定できます。例えば、サンプル下段の配列「arr_2」は添え字が「1」始まりの配列として宣言しています。

動的配列

宣言時に配列の大きさを指定せず、マクロの実行中に配列の要素数を書き換えることができる配列を動的配列(どうてきはいれつ)と呼びます。

宣言の方法

静的配列と異なり、宣言時には「0 To 3」のような要素数を指定しません。

サンプルコード(クリックでコピー)
Private Sub dim_dynamic_arr()

Dim arr_1() As String

Stop

End Sub
実行結果

上記サンプルコードを実行すると「Stop」で動作が停止するため、ローカルウィンドウで中身を見てみましょう。「arr_1」という動的配列を宣言していますが、要素数が決まっていないため中身のない配列となっています。

ReDim(動的配列の再定義)

動的配列の要素数を決めるには配列の再定義(ReDim)をします。

サンプルコード(クリックでコピー)
Private Sub ReDim_dynamic_arr()

Dim arr_1() As String
ReDim arr_1(1 To 3, 1 To 2)

Stop

End Sub
実行結果

上記サンプルコードを実行すると「Stop」で動作が停止するため、ローカルウィンドウで中身を見てみましょう。「arr_1」という動的配列を再定義しており、6(3×2)個のデータが格納できる配列になっています。

ReDimで再定義済の配列を再定義する

動的配列はマクロの実行中に何度でもReDimで再定義することができます。しかし、ReDimは配列に入力済のデータを初期化してしまいます。

サンプルコード(クリックでコピー)
Private Sub ReDim_dynamic_arr()

Dim arr_1() As String
ReDim arr_1(1 To 3, 1 To 2)

arr_1(1, 1) = "F0000001"
arr_1(2, 1) = "F0000002"
arr_1(3, 1) = "F0000003"

arr_1(1, 2) = "ユーザーフォームに最小化/最大化ボタンを表示させる"
arr_1(2, 2) = "文字列に含まれる特定のワードの出現回数を返す"
arr_1(3, 2) = "マクロ実行者のデスクトップパスを取得する"

Debug.Print arr_1(3, 2)

ReDim arr_1(1 To 3, 1 To 3)
arr_1(1, 3) = "2024/07/28"
arr_1(2, 3) = "2024/07/30"
arr_1(3, 3) = "2024/07/31"

Debug.Print arr_1(1, 1) & ":" & arr_1(1, 3)

End Sub
実行結果

上記サンプルコードでは、3×2の大きさの配列をReDimで再定義しています。
配列には、
 1列目 ⇒ 関数のID
 2列目 ⇒ 関数の名前
を代入し、入力確認のため3行・2列目の値をイミディエイトウィンドウに出力します。
ここで配列にデータを1列追加したくなり、ReDimで列の値を一つ増やして再定義しました。追加した3列目に公開日を代入し、同じように入力確認をすると先に入力していた内容が反映されませんでした。

入力済のデータを保持してReDimする

入力済のデータを保持したまま、ReDimで動的配列を再定義するには「Preserve」キーワードを使用します。

サンプルコード(クリックでコピー)
Private Sub ReDim_Preserve_dynamic_arr()

Dim arr_1() As String
ReDim arr_1(1 To 3, 1 To 2)

arr_1(1, 1) = "F0000001"
arr_1(2, 1) = "F0000002"
arr_1(3, 1) = "F0000003"

arr_1(1, 2) = "ユーザーフォームに最小化/最大化ボタンを表示させる"
arr_1(2, 2) = "文字列に含まれる特定のワードの出現回数を返す"
arr_1(3, 2) = "マクロ実行者のデスクトップパスを取得する"

Debug.Print arr_1(3, 2)

ReDim Preserve arr_1(1 To 3, 1 To 3)
arr_1(1, 3) = "2024/07/28"
arr_1(2, 3) = "2024/07/30"
arr_1(3, 3) = "2024/07/31"

Debug.Print arr_1(1, 1) & ":" & arr_1(1, 3)

End Sub
実行結果

前述のサンプルコードとの違いはReDimするときに「Preserve」を指定しているか否かです。今回はPreserveを指定しているため、再定義後も先に入力していたデータが保持されます。

配列の種類(次元数による違い)

配列には「次元」という概念があります。ここまでサンプルで取り上げてきた配列はすべて「2次元配列」です。2次元配列は要素を「,(カンマ)」で区切って宣言し、データを入れ子構造で格納できます。入れ子構造にしない1次元配列も実務では使用されることがあります。また逆に使用する機会はすくないですが、3次元配列や4次元配列も使用することができます。

いろいろな次元の配列

サンプルコード(クリックでコピー)
Private Sub MultiDimentionArr()

Dim multi_arr(0 To 5, 0 To 3, 0 To 2)
Stop

End Sub

Private Sub SingleDimentionArr()

Dim single_arr(0 To 5)
Stop

End Sub
実行結果

上記サンプルコードを実行すると、「Stop」で動作が停止します。ローカルウィンドウで中身を見てみましょう。以下はサンプルコード上段の実行結果です。

個人的な意見ですが・・・一般人には脳内で三次元のデータ構造を描いて記憶、処理することは簡単ではありません。そのため、3次元配列を使用しなければならないような場面であっても、2次元以下の配列に分割して管理するなど、あえて困難を避ける工夫をすることが有用(時間の節約や、ストレス軽減・・etc)だと思います。

多次元配列をReDim(Preserve)するときの注意点

動的配列はマクロの実行中に何度でもReDimで再定義することができます。またその際Preserveキーワードを指定すると入力済の値を保持したまま、配列のサイズを変更できます。ただし、多次元配列に対してこの操作をする場合、要素数の変更は最大の次元数にのみ有効です。例えば、2次元配列の1次元目の要素数はPreserveキーワードを指定していると変更することができません。

サンプルコード(クリックでコピー)
Private Sub PreserveError()

Dim multi_arr()

ReDim multi_arr(0 To 5, 0 To 3)

'■これはエラーにならない
ReDim Preserve multi_arr(0 To 5, 0 To 4)

'■これはエラーになる
ReDim Preserve multi_arr(0 To 6, 0 To 4)

End Sub
実行結果

上記サンプルコードを実行すると、1次元目の要素数を変更するコードを実行する際にエラーが発生します。

配列の要素数を取得する

実務では配列に格納する要素数がマクロの実行前に確定していないことが多いです。ユーザーの入力数やデータの読み取り件数に応じて動的配列を定義し、それを1レコードずつ処理するといった場面が想定されます。そんな時には、Lbound関数とUbound関数を使用します。

Lbound関数とUbound巻子

Lbound関数は指定した配列の最小の要素を取得します。
Ubound関数は指定した配列の最大の要素を取得します。

サンプルコード(クリックでコピー)
Private Sub LboundUbound()

Dim multi_arr()
Dim x As Long, y As Long, i As Long, j As Long

x = Application.InputBox("0以上の整数を入力する", "配列のベースインデックスを入力する", 0, 500, 300, , , 1)
If Not IsNumeric(x) Then Exit Sub

y = Application.InputBox("配列の要素数を0以上の値で入力する", "配列の要素数", 5, 500, 300, , , 1)
If Not IsNumeric(y) Then Exit Sub

ReDim multi_arr(x To x + y, x To x + y * 2)
Call Msgbox(LBound(multi_arr, 1) & " To " & UBound(multi_arr, 1) & vbCrLf & LBound(multi_arr, 2) & " To " & UBound(multi_arr, 2))

For i = LBound(multi_arr, 1) To UBound(multi_arr, 1) Step 1
For j = LBound(multi_arr, 2) To UBound(multi_arr, 2) Step 1
multi_arr(i, j) = i * j
Next j
Next i

Stop

End Sub
実行結果

上記サンプルコードを実行すると、入力値に応じて動的配列の再定義をします。配列の要素数をメッセージに表示し、その後、配列のすべての要素に1次元目と2次元目のインデックスを乗算した結果を格納します。「Stop」でマクロが停止するので、格納された内容はローカルウィンドウから確認してください。

Lbound関数とUbound関数の第二引数は配列の次元数です。省略した場合「1」が指定されたものとみなされます。

配列の要素に関する注意点

配列の要素数を指定するとき、ここまで「1 To 3」や「0 To 5」などのように配列の最小の要素と最大の要素を指定してきました。しかし要素数の指定は「3」や「5」などのように最小の要素を省略することができます。省略した場合、最小要素は「0」が指定されたものとみなされます。

サンプルコード(クリックでコピー)
Private Sub ReDimOmission()

Dim buf_arr(3)

Call Msgbox(LBound(buf_arr, 1) & " To " & UBound(buf_arr, 1))

End Sub
実行結果

上述のサンプルコードを実行すると、「buf_arr」の要素数をメッセージで表示します。最小の要素を省略した場合、「0」が指定されたものとみなすことが確認できます。

Option Base:[0|1]を指定することで、最小の要素を省略した時に「0」を指定されたものとみなすか、「1」が指定されたものとみなすかを制御することができます。

Split関数

VBAで用意されている関数の中には、配列を戻り値とするものもあります。Split関数は文字列を指定した文字列で分割して配列に格納する関数です。

サンプルコード(クリックでコピー)
Private Sub SplitTest()

Dim return_arr() As String

return_arr = Split("A0BC0DEF0H", 0)

Stop

End Sub
実行結果

上記サンプルコードを実行すると、「Stop」でマクロが停止します。配列には文字列が「0」で分割された状態で格納されています。

Split関数の戻り値となる配列は、必ず「0」から始まります。

【PR】VBAエキスパート試験対策記事

当サイトでは、オデッセイコミュニケーションズ社が運営する試験であるVBAエキスパートExcel VBA ベーシックExcel VBA スタンダード)の出題範囲をベースに用語や各種関数の解説などを行っています。試験合格に向けて必須と言われる公式テキストに沿って解説をしています。受験をするか悩んでいる方、テキストとは別視点の解説を見てみたい方、受験はしないがExcelVBA(マクロ)に興味がある方へ向けた記事です。

Comment