3
1

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 1 year has passed since last update.

[VBA]VBAの制御ステートメントは一行にまとめられる。

Last updated at Posted at 2021-11-09

VBAにおける制御ステートメントというと、If 〜 Then 〜 Else ステートメント、Select Case ステートメントがある。書き方としては次のとおり。

ifthenelse.vba
'If 〜 Then 〜 Else ステートメント
Sub IfThenElse()
    Dim digits As Long : digits = 3
    Dim msg As String
    If digits = 1 Then
       msg = "一桁です。"
    Else
       msg = "二桁以上です。"
    End if
End Sub
selectcase.vba
'Select Case ステートメント
Sub SelectCase()
    Dim rank As String: rank = "優"
    Dim msg As String

    Select Case rank
        Case "優"
            msg = "すごいですね!"
        Case "良"
            msg = "頑張りましたね"
        Case "可"
            msg = "ギリギリでしたね"
    End Select
End Sub

これはこれで読みやすいができれば簡単なものは一行にまとめてしまいたい。RubyとかではなんでもワンライナーにすることができるがVBAではそういうニーズには答えるようなものはないと思いこんでいた。

ところが、そういうワンライナー化のニーズに答える関数がVBAの標準ライブラリに存在した。それが、InteractionモジュールのIIf(Expression, TruePart, FalsePart)Switch(expr-1, value-1, [, expr-2, value-2, ・・・])という関数である。

ざっと書くと、上の制御ステートメントで記述したプログラムは次のような形に置き換えることができる。

IIf.vba
Sub IIfFunction()
    Dim digits As Long : digits = 3
    Dim msg As String
    msg = Interaction.IIf(digits = 1, "一桁です。", "二桁以上です。")
End Sub
Switch.vba
Sub SwitchFunction()
    Dim rank As String: rank = "優"
    Dim msg As String
    msg = Interaction.Switch(rank = "優", "すごいですね!", rank = "良", "頑張りましたね", rank = "可", "ギリギリでしたね")
End Sub

非常に記述がスッキリした形にすることができる。短い条件分岐であれば使用する価値はある。問題は、これは条件分岐のステートメントではなく関数であるので、引数に関数を入れた場合、全ての引数の関数を評価してしまうという弱点がある。

例を出して説明しよう。

sample.vba
Function Hello()
    MsgBox "Say Hello"
    Hello = "Hello"
End Function

Function World()
    MsgBox "Say World"
    World = "World"
End Function

Sub SideEffect()
    Dim msg As String
    msg = Interaction.IIf(True, Hello(), World())
End Sub

Say Helloという文字をメッセージボックスに表示させたあと"Hello"という文字列を返す関数Helloと同じくSay Worldという文字をメッセージボックスに表示させた後に"World"という文字列を返す関数Worldを事前に定義しておく。

IIfで条件分岐させるステートメントとしては、

Interaction.IIf(True, Hello(), World())

という常にTrueパートの部分のみを返す条件分岐となっている。期待される動作としては、これを実行すれば、メッセージボックスに"Say Hello"と出たあとに"Hello"という文字が返り、msg変数にその"Hello"という文字列が格納される、というものである。ところが、これは実際の動作とは異なる。実際に実行してみると、TrueパートのHello関数が評価されるところまでは正しいが、FalseパートのWorld関数も評価されて"Say Hello"とメッセージボックスに表示された後、"Say World"というメッセージボックスが条件がTrueであるかFalseであるかに関わらず表示されてしまうのである。

つまり、引数に入れた関数が内容として副作用を伴うものであると、条件がTrueかFalseであるかに関わらず副作用が評価されてしまうのである。なお、普通のIf 〜 Then 〜 Elseステートメントであればこういうことにならない。

以上を踏まえて、機械的にIf 〜 Then 〜 Elseステートメント、Select Casedステートメントを一行関数ステートメントに置き換えるのはおすすめできないが、使い方によっては、今までのVBAプログラムが結構スッキリと簡略化できるでしょう。

3
1
1

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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?