機能と数式 | VBA | セミナー | オンラインソフト | お問い合わせ | その他
Top > Excel > VBA > VBA高速化テクニック

個別に呼ばない



VBAの繰り返し命令(ステートメント)には、次の3種類があります。

  • For Next ステートメント
  • For Each ステートメント
  • Do Loop ステートメント

本当はもうひとつ

  • While Wend ステートメント

というのがあるんですが、これはほとんどDo Loopステートメントと同じなので、一般的にはDo Loopステートメントが使われます。あえて、While Wendステートメントを使う理由はありません。

さて、ここでは、

  • For Next ステートメント
  • For Each ステートメント

で、速度の違いを検証してみます。というのも、ほとんどの処理はどちらも書けるんです。たとえば、すべてのワークシートを操作するのなら、次の2つは同じ結果になります。

Sub Sample1()
    Dim ws As Worksheet
    For Each ws In Worksheets
        Debug.Print ws.Name
    Next ws
End Sub

Sub Sample2()
    Dim i As Long
    For i = 1 To Worksheets.Count
        Debug.Print Worksheets(i).Name
    Next i
End Sub

選択したセル範囲を操作するとき、一般的にはSelectionをFor Eachで回しますが、これもFor Nextで書けます。

Sub Sample3()
    Dim c As Range
    For Each c In Selection
        c.Value = "tanaka"
    Next c
End Sub

Sub Sample4()
    Dim i As Long
    For i = 1 To Selection.Count
        Selection(i).Value = "tanaka"
    Next i
End Sub

アクティブシート上に配置した、すべてのオートシェイプを削除するのなら、

Sub Sample5()
    Dim s As Shape
    For Each s In ActiveSheet.Shapes
        s.Delete
    Next s
End Sub

Sub Sample6()
    Dim i As Long
    For i = ActiveSheet.Shapes.Count To 1 Step -1
        ActiveSheet.Shapes(i).Delete
    Next i
End Sub

みたいに。
どちらがいいとか、ビギナーはFor Nextで、For Eachは上級者とかではありません。両方自由に使えるようになって、可読性やメンテナンス性を考慮して使い分けるのがベストです。ただ、For NextとFor Eachでは、実は少しだけ速度に差があります。

Sub Test1()
    Dim i As Long
    For i = 1 To 50000
        Cells(i, 1) = "tanaka"
    Next i
End Sub

Sub Test2()
    Dim c As Range
    For Each c In Range("A1:A50000")
        c = "tanaka"
    Next c
End Sub

Test1 Test2 %
1回目  2.12 秒  2.02 秒  95.6% 
2回目  2.06 秒  2.04 秒  98.8% 
3回目  2.07 秒  2.04 秒  98.5% 
4回目  2.06 秒  2.04 秒  98.7% 
5回目  2.12 秒  2.03 秒  95.6% 
6回目  2.17 秒  2.04 秒  93.8% 
7回目  2.16 秒  2.03 秒  94.0% 
8回目  2.13 秒  2.03 秒  95.4% 
9回目  2.07 秒  2.03 秒  98.0% 
10回目  2.06 秒  2.02 秒  98.0% 
平均  2.10 秒  2.03 秒  96.6% 

50000個のセルを操作して、この程度の差ですが、確かに少しだけFor Eachの方が速いです。これは、セルではなく、オートシェイプの操作だと、もう少し差が出ます。次のコードはそれぞれ、アクティブシートに配置した1000個のオートシェイプ(四角形)を削除しています。

Sub Test1()
    Dim i As Long
    For i = ActiveSheet.Shapes.Count To 1 Step -1
        ActiveSheet.Shapes(i).Delete
    Next i
End Sub

Sub Test2()
    Dim s As Shape
    For Each s In ActiveSheet.Shapes
        s.Delete
    Next s
End Sub

Test1 Test2 %
1回目  0.37 秒  0.28 秒  73.6% 
2回目  0.36 秒  0.26 秒  71.6% 
3回目  0.36 秒  0.27 秒  72.8% 
4回目  0.36 秒  0.26 秒  72.4% 
5回目  0.41 秒  0.27 秒  65.4% 
6回目  0.36 秒  0.27 秒  75.2% 
7回目  0.36 秒  0.26 秒  71.5% 
8回目  0.37 秒  0.27 秒  72.1% 
9回目  0.37 秒  0.27 秒  71.8% 
10回目  0.36 秒  0.27 秒  72.8% 
平均  0.37 秒  0.27 秒  71.9% 

まぁ、For Nextよりも若干For Eachの方が速いってことですね。
それ以外に、For Eachは可読性を高める効果もあります。たとえば、次のマクロをご覧ください。アクティブブックではないBook2.xlsxの全ワークシートを調べて、もしシート名に「2009」が含まれていたら、それを「2010」に置換するマクロです。まず、For Nextから。

Sub Sample7()
    Dim i As Long
    For i = 1 To Workbooks("Book2.xlsx").Worksheets.Count
        If InStr(Workbooks("Book2.xlsx").Worksheets(i).Name, "2009") > 0 Then
            Workbooks("Book2.xlsx").Worksheets(i).Name = _
                Replace(Workbooks("Book2.xlsx").Worksheets(i).Name, "2009", "2010")
        End If
    Next i
End Sub

ごちゃごちゃして見にくいですよね。もちろん、Withステートメントやオブジェクト型変数を使って、可読性を高めることも可能です。こんな風に。

Sub Sample8()
    Dim i As Long
    With Workbooks("Book2.xlsx")
        For i = 1 To .Worksheets.Count
            If InStr(.Worksheets(i).Name, "2009") > 0 Then
                .Worksheets(i).Name = _
                    Replace(.Worksheets(i).Name, "2009", "2010")
            End If
        Next i
    End With
End Sub

あるいは、

Sub Sample9()
    Dim i As Long, Target As Object
    Set Target = Workbooks("Book2.xlsx").Worksheets
    For i = 1 To Target.Count
        If InStr(Target(i).Name, "2009") > 0 Then
            Target(i).Name = Replace(Target(i).Name, "2009", "2010")
        End If
    Next i
End Sub

でもこれは、For NextではなくFor Eachを使うと全体がスッキリします。

Sub Sample10()
    Dim ws As Worksheet
    For Each ws In Workbooks("Book2.xlsx").Worksheets
        If InStr(ws.Name, "2009") > 0 Then
            ws.Name = Replace(ws.Name, "2009", "2010")
        End If
    Next ws
End Sub

このように、For Eachで可読性を高めるときは、制御変数(ここではws)に簡素な名前を使うのがポイントです。



このエントリーをはてなブックマークに追加