配列の次元数を取得するFunction

配列の次元数を取得する

何気なく配列をぐりぐりいじくっていたときに、ちょっとした間違いで

f:id:akashi_keirin:20180310094500j:plain

こんなエラーが出た。まあ、よくあるエラーなんだが、このときは要素数を超えたのではなくて、存在しない次元を指定していたのだった。

ということは、LBoundとかUBoundみたいな

配列の次元数(Dimension)を引数に持つ関数

に 1 づつインクリメントした数を渡していけば、配列次元数を超えたところでエラーが出るということだ。

というわけで、配列の次元数を取得するFunctionを作ってみた。

配列の次元数を返すFunction

リスト1 標準モジュール
Public Function getArrayDimension( _
                  ByRef targetArray As Variant) As Long    '……(1)'
  If Not IsArray(targetArray) _
    Then getArrayDimension = False: Exit Function    '……(2)'
  Dim n As Long    '……(3)'
  n = 0
  Dim tmp As Long
  On Error Resume Next    '……(4)'
  Do While Err.Number = 0
    n = n + 1
    tmp = UBound(targetArray, n)
  Loop
  Err.Clear
  getArrayDimension = n - 1    '……(5)'
End Function

(1)の

Public Function getArrayDimension(ByRef targetArray As Variant) As Long

では、引数と返り値を設定。どんな配列が渡されるか分からないので、Variant型。返り値はInteger型でも良いと思うのだけれど、100万次元ぐらいの配列が渡されるかも知れないので(←ねえよw)Longにした。

追記

id:imihito さんによると、配列の次元数は60が最大、とのこと(コメント欄参照)。要するに、非常にマヌケなことを書いてしまっているということですね。まあ、大は小を兼ねるわけですから、このままLongっちゅうことにしときます。

(2)の

If Not IsArray(targetArray) Then getArrayDimension = False: Exit Function

はガード節。IsArray関数を使って配列かどうかを判定し、配列でなければ即Falseを返してFunctionを抜ける。

返り値は別に「0」で良いと思うが、Falseとした方が分かりやすいと思った。

(3)からの2行

Dim n As Long
n = 0

では変数 n を用意して 0 で初期化。別にInteger型でも良いと思うのだけれど、100万(以下ry

この n は、後でインクリメントしてUBound関数に渡すために使う。

関係ないけれど、こういう野暮ったい書き方を見ると、

int n = 0;

みたいに書ける言語が羨ましくなる。

(4)からの6行

On Error Resume Next
Do While Err.Number = 0
  n = n + 1
  tmp = UBound(targetArray, n)
Loop
Err.Clear

まず

On Error Resume Next

でエラーが出ても処理を続行するように指定しておく。

Do While Err.Number = 0

としているので、Doループのブロック突入時にErrオブジェクトのNumberプロパティを調べて、「0」すなわちエラーが出ていなければブロック内に突入することになる。

ブロック内に突入すると、まず

n = n + 1

で n をインクリメントし、

tmp = UBound(targetArray, n)

でUBound関数の引数Dimensionに渡す。

この段階で n が引数で渡された配列targetArrayの次元数を超えていたらエラーが発生して次へ進むことになる。

ちなみに、変数tmpはUBound関数を使うための単なるアテ馬

Doループ内でエラーが発生したらループを抜けて、

Err.Clear

でErrオブジェクトをリセットしておく。

この段階で、変数 n には、引数で渡された配列targetArrayの要素数 + 1 が格納されているはずなので、(5)の

getArrayDimension = n - 1

をreturnしておしまい。

使ってみた

次のコードで実行。

スト2 標準モジュール
Public Sub testGetArrayDimension()
  Dim ar1(1, 1, 1) As String
  Debug.Print getArrayDimension(ar1)
  Dim ar2(1, 1, 1, 1, 1, 1, 1)
  Debug.Print getArrayDimension(ar2)
  Debug.Print getArrayDimension("ち~んw")
  Debug.Print Not getArrayDimension("ち~んw")
End Sub

リスト1のgetArrayDimensionを、それぞれ

  • 3次元配列を渡す――(1)
  • 7次元配列を渡す――(2)
  • 配列でないただの文字列を渡す――(3)
  • (3)の結果をNotする――(4)

形で実行している。

実行結果

f:id:akashi_keirin:20180310094507j:plain

意図したとおりの結果となった。

ちなみに、「0」はFalse、「ー1」はTrueなので、Not Falseの形でTrueも表現できる。

おわりに

相変わらず、何に使うのかは不明ですがw

@akashi_keirin on Twitter