VBA 수수께끼 사양 : For Each에서 지정하는 변수는 변수적이면 무엇이든 지정할 수 있으며 루프마다 평가됩니다.

16516 단어 VBA

소개



VBA에서 For Each 문을 사용하면 배열과 열거 가능한 개체의 요소를 반복할 수 있습니다.

이때 루프마다 각 요소가 저장되는 변수(아래 코드의 element .
For Each element In group
    statements
Next

각...Next 문(VBA) | Microsoft Docs
For Each...Next 문 사용(VBA) | Microsoft Docs

"변수적인 것"의 정의



이 기사에서 "변수적인 것"은 다음과 같이 정의됩니다.
ByRef が指定されたプロシージャの引数に指定したとき、プロシージャ内で変更された内容が反映される任意の式

구체적으로는, 임의의 변수, 배열의 요소, 유저 정의형의 필드등이 해당합니다.
프로퍼티로서 동작하는 것 ( Property 프로시저로 정의된 것, 표준 모듈 이외의 모듈의 Public 의 변수를 . 경유로 액세스 한 것)는 포함하지 않습니다.

변수적인 것
Type SampleUDT
    Value As Variant
End Type

Public varPublic As Variant

Sub VariableExpression()
    Dim arr() As Variant
    arr = VBA.[_HiddenModule].Array()

    '任意の変数。
    Dim varLocal As Variant
    For Each varLocal In arr

    Next varLocal

    For Each varPublic In arr

    Next varPublic

    '配列の要素。
    Dim varArr(0 To 0) As Variant
    For Each varArr(0) In arr

    Next varArr(0)

    'ユーザー定義型のフィールド。
    Dim varUdt As SampleUDT
    For Each varUdt.Value In arr

    Next varUdt.Value

    '以下はNG
    'For Each propValue In arr
    'Next

End Sub

Public Property Get propValue() As Variant

End Property


Public Property Let propValue(inValue As Variant)

End Property

실제로 확인



「배열의 요소」를 지정해 For Each 루프를 실시하는 샘플 코드입니다.
Sub SampleForEach()
    'VBA.Collection を新規インスタンスして、適当に中身を設定する。
    Dim col As VBA.Collection
    Set col = New VBA.Collection

    '文字列・整数・小数・日付・オブジェクト・Null を設定。
    col.Add "ABC"
    col.Add 123
    col.Add 3.14
    col.Add VBA.DateTime.DateSerial(2021, 3, 27)
    col.Add Err
    col.Add Null

    'Variant 型の配列を宣言し、VBA.Collection の要素数 + 1 だけ領域を確保する。
    Dim arr() As Variant
    ReDim arr(1 To col.Count() + 1)

    '配列の末尾に「Hoge」を設定する。
    arr(UBound(arr)) = "Hoge"

    Dim i As Long
    i = LBound(arr)

    For Each arr(i) In col
        i = i + 1
    Next arr(i)

    Stop
End Sub

이 코드에는 For Each arr(i) In col 라는 줄이 있으며 element 표시합니다.

실제로 For Each 의 시점에서의 로컬 윈도우의 내용을 확인하면, 이하의 화상과 같이 되어 있습니다.

i 까지는, Stop 의 내용이 그대로 격납되고 있어, arr(1 To 6) 의 열거 완료시에는 colcol 가 되어 있기 때문에, 초기화되어 i
덧붙여서, 배열 변수의 형태 선언으로부터 7 를 빼고, Empty 라고 하면(자), () 가 변수적인 것을 나타내는 보증이 없어지기 때문에, 컴파일 에러가 됩니다.


이것이 어떤 도움이 되는가?



변한 거동 때문에, 기본적으로는 도움이 되지 않는 것이라고는 생각합니다.

무리한 사용법을 생각하면, 「값과 오브젝트가 섞인 컬렉션을 Variant 배열에 격납한다」라고 하는 용도에서는, 일단 사용할 수 없지는 않습니다.

VBA에서는 변수적인 것에의 대입시에, 대입하는 것이 객체의 경우는 Dim arr As Variant그 때문에, 대입하기 전에 어느 쪽인지 판정한 다음에 대입 방법을 전환할 필요가 있습니다.

참고 : VBA 개인 범용 처리 - Qiita

그러나 배열의 요소를 For Each의 변수로 지정하면 VBA 내부에서 대입을 해 주므로 판정 처리를 쓸 필요가 없어지고 (자신이 시도한 범위에서는) 처리 속도도 향상되었습니다.
Sub SpeedTest()
    Const LoopCount = 1 * 10 ^ 7

    Dim col As VBA.Collection
    Set col = New VBA.Collection

    Dim i As Long
    For i = 1 To LoopCount
        col.Add i
    Next i

    Dim tim As Single

    Debug.Print "普通に配列へ代入",
    tim = VBA.DateTime.Timer
    Dim arr1() As Variant
    ReDim arr1(1 To col.Count)

    i = LBound(arr1)

    Dim v As Variant
    For Each v In col
        'Object かそれ以外かで、代入方法を切り替える。
            'この方法では問題になることがあるが、あまりない話なので省略。
            'https://qiita.com/nukie_53/items/bde16afd9a6ca789949d#%E5%A4%89%E6%95%B0%E3%81%B8%E4%BB%A3%E5%85%A5
        If VBA.Information.IsObject(v) Then
            Set arr1(i) = v
        Else
            Let arr1(i) = v
        End If
        i = i + 1
    Next v

    Debug.Print VBA.Strings.Format$(VBA.DateTime.Timer - tim, "0.000")

    Debug.Print "For Eachで配列に代入",
    tim = VBA.DateTime.Timer
    Dim arr2() As Variant
    ReDim arr2(1 To col.Count)

    i = LBound(arr1)
    Dim limitIndex As Long
    limitIndex = UBound(arr2)
    For Each arr2(i) In col
        'For Each が正常終了すると、最後の要素が Empty になってしまうことへの対策。
        If i >= limitIndex Then Exit For
        i = i + 1
    Next arr2(i)

    Debug.Print VBA.Strings.Format$(VBA.DateTime.Timer - tim, "0.000")

End Sub

기타



#VBA퀴즈 립으로 낮추고 있는 이미지의 코드를 실행했을 때, Stop 스테이트먼트의 위치에서, 배열 변수 b 의 내용은 어떻게 되어 있습니까? — 이미히토 (@nukie_53) 10월 14, 2020

좋은 웹페이지 즐겨찾기