1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Collection は For Each 中でも要素を足してみよう

Last updated at Posted at 2019-04-07

Collection と For Each

VBA では、要素数が未知の場合にまずVBA.Collectionを使いますね。
そんなVBA.Collectionですが、先頭要素からの線形リストになっているらしく、以降の要素にもアクセスする場合はひとつひとつ添え字の指定をするのは非効率なのでFor Eachで順次アクセスするのが一般的です。

ただしVBA.Collectionには、C#のyieldのように要素を1つ求めた時点で取得処理に処理を戻すような機構がありません。
そのため、要素を一括で作成してからFor Eachを開始するような書き方をよくします。

AfterAllAdd
    Dim TestColl As New VBA.Collection
    
    Dim i As Long
    For i = 1 To 5
        Call TestColl.Add("Value" & Format(i, "00"))
    Next i
    
    Dim vItem As Variant
    For Each vItem In TestColl
        Debug.Print vItem
    Next vItem

この場合、例えば要素が100個のものに対して使うのが30個までだった場合などに、残りの70個の作成処理が無駄になってしまいます。

Collection の末尾の判定

そんなVBA.Collectionですが、For Eachの中でループを継続するかどうかの判定は毎回行っているため、For Eachの途中で要素を追加・削除することができます。

CollectionAddInLoop
    Dim TestColl As New VBA.Collection
    
    Call TestColl.Add("A")
    Call TestColl.Add("B")
    
    Dim vItem As Variant
    For Each vItem In TestColl
        Debug.Print vItem
        Select Case vItem
        Case "A"
            Call TestColl.Add("C")
            Call TestColl.Add("D")
        Case "C"
            Call TestColl.Add("E")
        End Select
    Next vItem

実行結果

A
B
C
D
E

ただし、末尾の要素までたどりついてしまうとそこから末尾に足してもループの継続は行われなくなります。

CollectionAddOnTail
    Dim TestColl As New VBA.Collection
    
    Call TestColl.Add("A")
    Call TestColl.Add("B")
    
    Dim vItem As Variant
    For Each vItem In TestColl
        Debug.Print vItem
        Select Case vItem
        Case "A"
            Call TestColl.Add("C")
        Case "C"
            Call TestColl.Add("D")
        End Select
    Next vItem

実行結果

A
B
C

##ジョブキューとしての Collection
ループの中で要素を追加していく例としてジョブキューを考えてみます。

JobQueue_Try
Private Enum JobType
    Job_A
    Job_B
    Job_C
End Enum

Sub Main()
    Dim JobQueue As New Collection
    Call JobQueue.Add(Job_B)
    Call JobQueue.Add(Job_C)
    
    Dim CurJob As Variant 'As JobType
    For Each CurJob In JobQueue
        Select Case CurJob
        Case Job_A
            Call JobA(JobQueue)
        Case Job_B
            Call JobB(JobQueue)
        Case Job_C
            Call JobC(JobQueue)
        End Select
        Call JobQueue.Remove(1)
    Next CurJob
End Sub

Private Sub JobA(ByVal JobQueue As Collection)
    Debug.Print "Job.A"
    Call JobQueue.Add(Job_C)
End Sub

Private Sub JobB(ByVal JobQueue As Collection)
    Debug.Print "Job.B"
    Call JobQueue.Add(Job_C)
    Call JobQueue.Add(Job_A)
End Sub

Private Sub JobC(ByVal JobQueue As Collection)
    Debug.Print "Job.C"
End Sub

実行結果

Job.B
Job.C
Job.C
Job.A

Job.A の後には Job.C が続いてほしいのですが、Job.A に差し掛かった時点で For Eachが末尾に到達したために、続きの Job.C が足されたことが認識されませんね。

こういう場合は、要素を追加したときにダミーでさらにもう1要素足しておくような使い方がいいのかもしれません。

JobQueueWithDummy
Private Enum JobType
    NOP
    Job_A
    Job_B
    Job_C
End Enum

Sub Main()
    Dim JobQueue As New Collection
    Call JobQueue.Add(Job_B)
    Call JobQueue.Add(Job_C)
    
    Dim CurJob As Variant 'As JobType
    For Each CurJob In JobQueue
        Select Case CurJob
        Case NOP
            '
        Case Job_A
            Call JobA(JobQueue)
        Case Job_B
            Call JobB(JobQueue)
        Case Job_C
            Call JobC(JobQueue)
        End Select
        Call JobQueue.Remove(1)
    Next CurJob
End Sub

Private Sub JobA(ByVal JobQueue As Collection)
    Debug.Print "Job.A"
    Call JobQueue.Add(Job_C)
    Call JobQueue.Add(NOP)
End Sub

Private Sub JobB(ByVal JobQueue As Collection)
    Debug.Print "Job.B"
    Call JobQueue.Add(Job_C)
    Call JobQueue.Add(Job_A)
    Call JobQueue.Add(NOP)
End Sub

Private Sub JobC(ByVal JobQueue As Collection)
    Debug.Print "Job.C"
End Sub

実行結果

Job.B
Job.C
Job.C
Job.A
Job.C

ジョブキューの場合は途中にダミー要素があってもほとんど影響ありませんが、作られたCollectionの要素一式に意味があるものの場合は、その内容に応じて末尾の要素での追加を避けるしくみを考える必要がありますね。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?