配列

配列関係の関数一覧

既定最小添字の設定 Option Base
宣言 Public, Private, Static(Sub,Function内)
配列を作成 Dim, Redim, Array
配列かどうかの判定 IsArray
サイズ確認 LBound, UBound
初期化 Erase
文字列との相互変換 Join, Split

 

動的配列

要素数を確定しないで宣言した配列を動的配列と呼びます。次のように2通りの書き方があります。

Dim s() As String '方式1
ReDim s(3)

Dim s As Variant '方式2
ReDim s(3)

'↓ なお、VBAではこの書き方はできません。
'Dim s As String() '型のほうに()を付ける形式はVBAではNG!

 

ReDim時の型変更

Dim s() As String
'ReDim s(3) As Integer 'これはエラー。型を変更することはできません。
Dim v2() As Variant
'ReDim v2(3) As String 'これはNG!
ReDim v2(3) As Variant 'これはOK!
Dim v1 As Variant 
ReDim v1(3) As String 'これはOK 
ReDim v1(3) As Integer 'これもOK! 
ReDim v1(3) As Variant 'これもOK!!

 

動的配列の初期化判定

ReDim前にLBound,UBoundを使うとエラーとなります。LBound,UBoundは動的配列が初期化されていることを確認してから使う必要があります。

動的配列をReDimで初期化されているかどうかを判定するのは簡単ではありません。次のような、少し裏技的な方法で判断します。(※ なお、以前はSgn関数を使った方法が良く使われましたが、最近(2017以降?)のVBAではエラーとなるようですので使わないほうが良いでしょう。)

If (Not Not s) Then
    '初期化されていない
End If

なぜ、この方法で判定できるか実験してみましょう。

    Dim s() As String
    
    Debug.Print "ReDim前"
    Debug.Print "Not s = " & Not s
    Debug.Print "Not Not s = " & Not Not s
    
    ReDim s(1) ’初期化
    
    Debug.Print ""
    Debug.Print "ReDim後" 
    Debug.Print "Not s = " & Not s
    Debug.Print "Not Not s = " & Not Not s

これを実行すると、次のように表示されます。

ReDim前
Not s = -1
Not Not s = 0

ReDim後
Not s = -618696945
Not Not s = 618696944

VBAでは0をFalse、それ以外をTrueと解釈しますので、「Not Not s」で初期化済みかどうかを判定できるという仕掛けです。

 

初期化済み判定関数

Not Not で判定できることが分かりましたので、それを関数にしてみます。どんな型の配列も受け取れるようにVariant型の変数を引数にしています。

Private Function ArrayIsInited_(v As Variant) As Boolean
    ArrayIsInited_ = (Not Not v)
End Function

ところが、この関数を呼び出すと、「Not Not v 」の箇所でエラーとなってしまいます。どうやら、Variant型の変数(配列ではない)を「Not v」とするとエラーとなってしまうようです。

以下のように、引数を文字列の配列にすると、文字列の配列に関してはうまく作動します。ただし、文字列以外の配列は受け取れません。

Private Function ArrayIsInited_(s() As String) As Boolean
    ArrayIsInited_ = (Not Not s)
End Function

では、次ようにVariant型の配列にすればいいのでは?と思うかもしれませんが、こっちはVariant型の配列しか受け取れません。

Private Function ArrayIsInited_(v() As Variant) As Boolean
    ArrayIsInited_ = (Not Not v)
End Function

 

結局、関数にしようとすると、Variant型配列専用、String型配列専用、Integer型配列専用と、配列の型毎に関数が必要となってしまいます。

今のところ、これを解決する妙案は思い浮かびません。関数にするのはあきらめるしかないでしょう。

 

追記

この他にも、ReDim前にLBound,UBoundを使うとエラーが発生することを逆手に取り、エラーハンドリングする方法もあります。この方法だと配列の型を気にすることもなく汎用的な関数が書けます。