簡単な分岐処理にはIIf関数が便利です。
どうしても5行かかってしまうIf~Else~EndIfの分岐を、
以下のように書き換えることができます。
If A > 0 Then 結果 = "OK" Else 結果 = "NG" End If ' ⇩ 結果 = IIf(A > 0, "OK", "NG")
ただ、このIIf関数はちょっと遅いという弱点があるため、
それがどの程度なのかを検証してみます。
はじめに ~ IIf関数がTrue/Falseを両方計算する遅さ ~
IIf関数は「判定がTrueでもFalse部分も計算される」という特徴があります。
よって
' Ifステートメント If A > 0 Then 結果 = 10秒かかって取得するデータ Else 結果 = 20秒かかって取得するデータ End If ' IIf関数 結果 = IIf(A > 0, 10秒かかるデータ, 20秒かかるデータ)
このコードはIfステートメントが10秒または20秒で終わるのに対し、
IIf関数は両方合わせた30秒を必要とします。
時間がかかる処理をスキップするための分岐にはIIf関数は使用できませんので、
まずはそれを知っておいてください。
その上で今回は、
「ほぼ無視できる時間で取得できる値を使った場合にそれでも発生する差」
を検証します。
そしてお忙しい方のために先に結論から述べますと、
「どちらも1,000万回やって1秒かからないため、
普通に使う上では差を実感することはない。」
が結論です。
検証の詳細が見たい方は読み進めてみてください。
基本の検証
検証コード
Timer関数で計測します。
1,000万回の処理を10回行い平均値を計算します。
※ 中身は「検証コード」だけ読めばOKです。
前後は測定用のコードです。
Sub IfステートメントvsIIf関数() Dim Arr処理時間記録(1 To 10) As Double Dim 開始時刻 As Double, 終了時刻 As Double Dim 検証回数 As Long For 検証回数 = 1 To 10 開始時刻 = Timer Dim 代入先 As Long Dim 代入候補A As Long: 代入候補A = 1 Dim 代入候補B As Long: 代入候補B = 2 Dim 判定変数 As Long: 判定変数 = 0 Dim i As Long For i = 1 To 10000000 ' ◇ 検証コード(対象以外をコメントアウトして実行) ' ① Ifステートメント If 判定変数 > 0 Then 代入先 = 代入候補A Else 代入先 = 代入候補B End If ' ② IIf関数 代入先 = IIf(判定変数 > 0, 代入候補A, 代入候補B) ' ③ 自作IIf関数Long型 代入先 = 自作IIf_Long(判定変数 > 0, 代入候補A, 代入候補B) ' ④ 自作IIf関数Variant型 代入先 = 自作IIf_Variant(判定変数 > 0, 代入候補A, 代入候補B) Next 終了時刻 = Timer Arr処理時間記録(検証回数) = 終了時刻 - 開始時刻 Next Debug.Print WorksheetFunction.Average(Arr処理時間記録) End Sub Function 自作IIf_Long(判定 As Boolean, True時 As Long, False時 As Long) As Long If 判定 Then 自作IIf_Long = True時 Else 自作IIf_Long = False時 End If End Function Function 自作IIf_Variant(判定 As Boolean, True時, False時) If 判定 Then 自作IIf_Variant = True時 Else 自作IIf_Variant = False時 End If End Function
①Ifステートメントと②IIf関数がメインの比較ですが、
IIf関数がなぜ遅いのかを考察するために③④を加えました。
- ③の遅さ=Functionを呼ぶ遅さ(いわゆるオーバーヘッド)
- ④の遅さ=Variant⇔Longの相互変換の遅さ
ということになります。
結果
以下の通りの処理時間となりました。
①Ifステートメント | 0.09秒 |
②IIf関数 | 0.63秒 |
③自作IIf関数Long型 | 0.54秒 |
④自作IIf関数Variant型 | 0.99秒 |
※ Windows10、Intel Core i5-10400、メモリ8G、Excel365
Ifステートメントが圧勝で、IIf関数の7倍のスピードになります。
と、倍率に着目すると思ってしまうのですが、
1,000万回やって全員が1秒を切っています。
1行につき10回判定するそこそこ複雑な処理でも、
104万行埋め尽くされたブックでも1秒に満たない差ということです。
なので普通のExcel業務では同速と言っても差し支えないですね。
巡回セールスマン問題を解かせるとか、
そういうアルゴリズムを扱わない限り実感できる差は出ないと思います。
また、自作したIIf関数(中身はIfステートメント)を見てみると、
型指定すると早くはなりますが、ほぼIIf関数と同速です。
つまり
- IIf関数が遅い原因はほぼFunctionを呼ぶ遅さと同じ
- SubをCallしたりFunctionを使うことのタイムロスも同様に無視して良い」
ということもついでにわかります。
とりあえず基本の検証はこんなところです。
「普通に使う分には速度差は無視していい」
ということですので、他の要因でどちらを使うか決めてください。
5行 ⇒ 1行 という絶大なコードの削減効果がありますので、
IIfで見やすく書ける箇所はすべてIIfで書いていいと思います。
ベタ打ちの文字列を渡す場合
検証コード
続いて分岐する値をベタ打ちしてみます。
(Timerを管理するコードは同じですので割愛します)
' ① Ifステートメント If 判定変数 > 0 Then 代入先 = "候補A" Else 代入先 = "候補B" End If ' ② IIf関数 代入先 = IIf(判定変数 > 0, "候補A", "候補B") ' ③ 自作IIf関数String型 代入先 = 自作IIf_String(判定変数 > 0, "候補A", "候補B") ' ④ 自作IIf関数Variant型 代入先 = 自作IIf_Variant(判定変数 > 0, "候補A", "候補B")
変数だった先ほどと違い、逐一文字列を生成するためちょっと遅くなります。
この影響を見てみましょう。
結果
ベタ打ち文字 | 数値変数 | |
①Ifステートメント | 0.39秒 | 0.09秒 |
②IIf関数 | 1.77秒 | 0.63秒 |
③自作IIf関数Long型 | 1.83秒 | 0.54秒 |
④自作IIf関数Variant型 | 2.12秒 | 0.99秒 |
Ifステートメントが0.4秒、他が1秒強遅くなっています。
前述の通りIIf関数はTrueでもFalseまで計算するため、
1回あたり2つの文字列を作る必要があります。
確かに追加される処理時間が2倍強になっていますね。
ただ、これも実感できる差には至っていません。
ベタ打ちと変数の差も特段気にしなくて良いということですね。
セル値を渡す場合
検証コード
最後にセル値を判定に使ってみます。
' ① Ifステートメント If 判定変数 > 0 Then 代入先 = Range("A1") Else 代入先 = Range("B1") End If ' ② IIf関数 代入先 = IIf(判定変数 > 0, Range("A1"), Range("B1")) ' ③ 自作IIf関数String型 代入先 = 自作IIf_String(判定変数 > 0, Range("A1"), Range("B1")) ' ④ 自作IIf関数Variant型 代入先 = 自作IIf_Variant(判定変数 > 0, Range("A1"), Range("B1"))
結果
Range | 数値変数 | |
①Ifステートメント | 67秒 | 0.09秒 |
②IIf関数 | 111秒 | 0.63秒 |
③自作IIf関数Long型 | 135秒 | 0.54秒 |
④自作IIf関数Variant型 | 109秒 | 0.99秒 |
だいぶ遅くなりましたね。
IfステートメントとIIf関数の差を無視できるくらい遅いため、
ここまでくると処理時間がちょうど倍くらいになります。
1,000万回の読込というと、100万行×10列というくらいですね。
10万行だったらIf-7秒とIIf-11秒くらいの差になります。
一応Ifステートメントの方が早いと言えば早いのですが、
これは流石にIIf⇒IfではなくRange⇒配列を検討した方が良いですね。
配列にすることで変数と同等の読込速度にできますし、
その速度であれば、別にIIf関数で問題ありません。
以上でIfステートメントとIIf関数の速度検証を終わります。
両者の差は普通の業務では無視していい程度ですので、
書きやすさ、読みやすさでどちらを使うか決めてください。
5行 ⇒ 1行 という絶大なコードの削減効果がありますので、
IIf関数で見やすく書ける箇所はすべてIIf関数で書いていいと思います。
逆にIIf関数にしたせいで読みづらくなりそうならIf~Elseで書きましょう。
読みやすさ・書きやすさで決めておけば間違いないと思います。