和風スパゲティのレシピ

日本語でコーディングするExcelVBA

For文をたくさん分割しても処理は遅くならない

マクロの処理速度に関するお話です。

今回のテーマは「錯覚しやすいForステートメントの処理速度」です。

For文をたくさん分割しても処理は遅くならない

↓のようなマクロがあったとします。

For R = 1 To 10000

    処理A : 10行くらい

    処理B : 10行くらい

    処理C : 10行くらい

    処理D : 10行くらい

Next

それぞれの処理は、
Cells(R, ○)を計算に使うような処理だと思ってください。

全部で40行くらいの、「For文による行ごとのデータ処理」ですね。


また、それぞれの処理の関係は、

  • 処理Bは処理Aの後でやる必要がある
  • 処理Cと処理Dは一緒にやる必要がある

だったとしましょう。


この時、

For R = 1 To 10000
    処理A
Next

For R = 1 To 10000
    処理B
Next

For R = 1 To 10000
    処理C
    処理D
Next

↑こういう風にマクロを書き換えたとします。

なんか無駄なことしてて遅そう…。



ですが、よーく考えてみてください。


For文1個の書き方と、For文3個の書き方は、
なんと「Rに1を足す×20000回」しか処理の内容に差がありません

足し算20000回とか、処理時間を計測できませんので、差は0です。


なんか人間の感覚だと、1から10000まで行ったり来たりしてて
すごい2度手間で、処理が遅くなる気がしてしまいますが、
錯覚なんです。

スピード面では、両者は誤差どころかほぼ0なので、
For文をまとめるのに速度的な意味がないことは、まずは知っておきましょう。

For文を分けたほうが変更に強い

For R = 1 To 10000
    処理A
Next

For R = 1 To 10000
    処理B
Next

For R = 1 To 10000
    処理C
    処理D
Next

この書き方は、実はかなりいい書き方です。

どの処理とどの処理に関連があるのかが、見ただけで分かるからです。


「処理D」を書き換える必要が発生したとしましょう。

「処理AとBは見なくてOK。処理Cはしっかり見ること。」というのが明示されていますね。


ループ文の中身は、複雑なことも多々あります。

特に「処理の順番が大事」だったりすると、
改修時にループ全体を見渡す必要が出てしまいます。

それを軽減しているのですね。


独立したコードは、独立しているとわかるように書くと、
ロジックの掴みやすい、読みやすいコードを書くことができます。

理想はプロシージャ分割

せっかく「独立したコード」が書けたので、
さらにいい書き方をしましょう。

Call データの全行を処理Aする

Call データの全行を処理Bする

Call データの全行を処理Cと処理Dする

と、プロシージャに分けるのです。


処理A,B,C,Dは実際は10行の処理なので、
全体で40行になっており、処理Aからは処理Dが見えません。

プロシージャに分けると、A,B,C,Dの処理順が一望できるので、
処理の流れが格段につかみやすくなります。


また、各プロシージャの中で、それぞれ専用のFor文がループするため、

Sub データの全行を処理Aする
    For R = 1 To 10000
    
        If is処理の中断条件を満たす Then
            Exit ○○ ' ←これをSubにするかForにするか選べる
        End If
    
    Next

    処理Aの最後の処理 ' Exit For なら実行される。Exit Subなら実行されない。
End Sub

このように「Exit For」や「Exit Sub」を、処理A専用で使うことができます。

分岐の処理や処理の中断判定を、かなりキレイに書く道具になります。


ループ部分を丸ごと関数化」は、
とても大事な関数分けポイントなので意識しておきましょう。

おまけ:本当に足し算分しか差は出ないのか?

本当に足し算だけの差なのでしょうか?
何とかして早くできないかな。


実は、「ループが1つ」なのを利用した高速化はあります。

処理Aの段階で、よく使う値を変数に入れておき、
それを処理B,C,Dでも使う
方法です。


頻出の値を、

現ループの売上 = Cells(R, 2)

と変数に入れておいて、
セル値ではなく、変数「現ループの売上」を処理B,C,Dでも使えば、

ワークシートのR行目を参照
  ⇩
変数を参照

に、処理の内容を変えることができます。

参照先がオブジェクトからメモリに代わるので、
実際、少しですが早くはなります。(ほんの少しね)



ただし!



これは高速化の手法としては最低最悪です。

本来独立であるはず処理を、互いに影響が出るように、結合してしまっているからです。


たとえば、「処理Aで求める値が(税込)に仕様変更」されたとしましょう。

現ループの売上 = Cells(R, 2) * 1.1

無意味な結合に気づかず、↑こんな風にコード書き換えてしまうと、
処理B,C,Dが全滅します。


こういうことを繰り返した結果、
「どこを変えるとどこをに影響があるかわからなくなってしまった」コードを、
からまってしまったコード、俗にスパゲティコードと呼ぶのです。


独立した処理は、ちゃんと独立を保ちましょう。

パスタはペンネもおいしいですよ。
ペンネパスタ