このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
昨日の続き。要素の追加をやろう。
ArrayList は可変長であるため、
要素数を決め打つことはできず、
必要に応じて拡張していく必要がある。
そのため要素の追加でやることは 2 つ。
・必要に応じて容量を拡大する
・内部配列に要素を格納する
このうち容量を拡大するのは、専用のメソッド
EnsureCapacity() で行うのでそれを先に作成する
Public Sub EnsureCapacity(ByVal MinimumCapacity As Long)
Dim lNew As Long
If Capacity >= MinimumCapacity Then Exit Sub
lNew = Capacity * 2
If lNew = 0 Then lNew = DEFAULT_CAPACITY
If lNew < MinimumCapacity Then lNew = MinimumCapacity
ReDim Preserve m_vValues(0 To lNew - 1) As Variant
End Sub
容量の拡大のタイミングの制御は難しいので、今回は適当で。
VBA の配列は、ReDim Preserve ステートメントを使うと、
内容を維持したまま容量を拡大することができる。
要素の追加は、内部で EnsureCapacity を呼び出せば、
要素を格納する処理だけを考えれば済む。
Public Function Add(ByVal Value As Variant) As Long
Call EnsureCapacity(m_lCount + 1)
If IsObject(Value) Then
Set m_vValues(m_lCount) = Value
Else
m_vValues(m_lCount) = Value
End If
Add = m_lCount
m_lCount = m_lCount + 1
End Function
VBA の厄介な所は、値型と参照型の扱いが異なることだ。
特に代入の時にそれが表面化する。
値型の代入は Let ステートメント、
参照型の代入式は、Set ステートメントとなる。
Variant 型は内部に何でも格納できるが、
値を代入する際は、この差を考える必要がある。
なお、Let は省略できるので、
値型の代入は、構文上は他の言語の代入と変わらない。
参照型かどうか確認するためには、
IsObject 関数を使えばよい。
次は、要素の挿入・削除を考えてみる。
途中に値を挿入した場合、以降の要素をずらす必要がある。
削除の場合も同様に必要だ。
つまり、要素を 1 つずらすヘルパメソッドが要る。
ヘルパメソッドは外部から呼ばれることはないため、
シグネチャ(引数や戻り値の規定)は
勝手な都合で作成できる。
ここでは、First, Last, Direction としておこう。
First, Last は名前通り、Direction は、1 か -1 でいい。
Private Sub ShiftElements(ByVal First As Long, ByVal Last As Long, ByVal Direction As Long)
Dim i As Long
For i = First To Last Step -Direction
If IsObject(m_vValues(i)) Then
Set m_vValues(i + Direction) = m_vValues(i)
Else
m_vValues(i + Direction) = m_vValues(i)
End If
Next
End Sub
これを利用すれば、挿入や削除も楽に実装できる。
Public Sub Insert(ByVal Index As Long, ByVal Value As Variant)
Dim i As Long
If Index < 0 Or Index > m_lCount Then
Call Err.Raise(9)
End If
Call EnsureCapacity(m_lCount + 1)
If Index < m_lCount Then
Call ShiftElements(m_lCount - 1, Index, 1)
End If
If IsObject(Value) Then
Set m_vValues(Index) = Value
Else
m_vValues(Index) = Value
End If
m_lCount = m_lCount + 1
End Sub
値を挿入する場合インデックスの範囲に注意する必要がある。
必要以上に配列を拡張するのを避けるため、
内部配列は実際の要素数より大きく割り当てている。
そのため、内部配列に代入する際には、
内部配列のエラーに頼ることができないため、
プログラム側で範囲チェックを行なう必要がある。
配列の境界のエラー番号は、VBA で 9 と定められている。
定数として定められていないので気持ち悪いが、
Err.Raise(9) で強制的にエラーを発生させることができる。
VBERROR_INDEX_OUT_OF_RANGE など定数化してもいいかも。
要素の削除も基本的には同じだ。
Public Sub RemoveAt(ByVal Index As Long)
Dim i As Long
If Index < 0 Or Index >= m_lCount Then
Call Err.Raise(9)
End If
If Index < m_lCount - 1 Then
Call ShiftElements(Index + 1, m_lCount - 1, -1)
End If
m_lCount = m_lCount - 1
m_vValues(m_lCount) = Empty
End Sub
削除した要素には、Empty を代入しておく。
参照型の参照が残らないようにだ。
Empty は Variant 専用の特殊型であり「空」を意味する。
Empty は値型のキーワードだ。