t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA 「真理値表を出力するプログラム」を出力するプログラム

プログラムを組んでいると、「フラグAがTrueのとき、フラグBの状態を反転させたい」というシチュエーションに出くわすことがある。

If文を使うと次のように書ける。

If フラグA Then
    フラグB = Not フラグB
End If

しかし、Xorを使用すると、次のようにIfを使わなくても1行で完結に記述することができる。

フラグB = フラグA Xor フラグB

こういう書き方が良いかどうかは別として、慣用的に使われている手法なので覚えておくと良い。(VBAではあまり見かけない)

真理値表

このコードがきちんと要件を満たしているか、表を使って確認してみよう。
以下のように、値1がTrueの場合のみ、値2が反転した結果が返ることがわかる。

Xorの真理値表

値1 値2 結果 値2と結果の比較
True True False 反転
True False True 反転
False True True なし
False False False なし

上記のように論理演算にTrueとFalseの全組み合わせを与えて表にしたものを真理値表という。

真理値表を出力するプログラム

さて、VBAには(And、Or、Not、Xor、Eqv、Imp)の6つの論理演算子がある。
最初の3つは比較的よく使うのだが、残りの3つは概念からしてやや難しく、使いどころも難しい。
そこで、真理値表でどういう演算なのかを確かめてみる。

たとえばImp演算の真理値表を出力してみる。

Sub Imp演算真理値表()
    Debug.Print "--Imp--"
    Debug.Print "T:T", True Imp True
    Debug.Print "T:F", True Imp False
    Debug.Print "F:T", False Imp True
    Debug.Print "F:F", False Imp False
End Sub

出力結果はこのようになる。

--Imp--
T:T    True
T:F    False
F:T    True
F:F    True

真理値表を出力するプログラムを出力するプログラム

プログラムで真理値表を出力するのは簡単であるが、別の演算子の真理値表を出力したいと思ったときにいちいち演算子を書き換えるのは面倒くさい。

そこでまず、次のようなプログラムを作成した。

Sub 真理値表()
    Debug.Print "Sub 真理値表出力()"
    For Each x In Split("And Or Xor Imp Eqv")
        Debug.Print vbTab & "Debug.Print  ""--" & x & "--"""
        Debug.Print vbTab & "Debug.Print ""T:T"", True " & x & " True"
        Debug.Print vbTab & "Debug.Print ""T:F"", True " & x & " False"
        Debug.Print vbTab & "Debug.Print ""F:T"", False " & x & " True"
        Debug.Print vbTab & "Debug.Print ""F:F"", False " & x & " False"
        Debug.Print vbTab & "Debug.Print"
    Next
    Debug.Print "End Sub"
End Sub

これを実行すると、イミディエイトウインドウに次のようなプログラムコードが出力される。

Sub 真理値表出力()
    Debug.Print  "--And--"
    Debug.Print "T:T", True And True
    Debug.Print "T:F", True And False
    Debug.Print "F:T", False And True
    Debug.Print "F:F", False And False
    Debug.Print
    Debug.Print  "--Or--"
    Debug.Print "T:T", True Or True
    Debug.Print "T:F", True Or False
    Debug.Print "F:T", False Or True
    Debug.Print "F:F", False Or False
    Debug.Print
    Debug.Print  "--Xor--"
    Debug.Print "T:T", True Xor True
    Debug.Print "T:F", True Xor False
    Debug.Print "F:T", False Xor True
    Debug.Print "F:F", False Xor False
    Debug.Print
    Debug.Print  "--Imp--"
    Debug.Print "T:T", True Imp True
    Debug.Print "T:F", True Imp False
    Debug.Print "F:T", False Imp True
    Debug.Print "F:F", False Imp False
    Debug.Print
    Debug.Print  "--Eqv--"
    Debug.Print "T:T", True Eqv True
    Debug.Print "T:F", True Eqv False
    Debug.Print "F:T", False Eqv True
    Debug.Print "F:F", False Eqv False
    Debug.Print
End Sub

あとはこれをコピペして実行すれば、次のような結果が得られる。

--And--
T:T    True
T:F    False
F:T    False
F:F    False

--Or--
T:T    True
T:F    True
F:T    True
F:F    False

--Xor--
T:T    False
T:F    True
F:T    True
F:F    False

--Imp--
T:T    True
T:F    False
F:T    True
F:F    True

--Eqv--
T:T    True
T:F    False
F:T    False
F:F    True

注意点として、絶対にプログラムを間違えないこと。
私は最初に書いたとき、以下のように最後のF:Fが上からコピーしたままFalse, Trueのままにしてしまうミスを犯してしまい、真理とかけ離れた偽値表ができてしまった。

Debug.Print vbTab & "Debug.Print ""F:F"", False " & x & " True"

メタプロ

今回取り上げたようなマクロでマクロを出力するテクニックをメタプログラミングという。

コードを文字列として取り扱う際、面倒くさいのがダブルクォーテーションの扱いである。

これも次のような小道具を用意しておくと便利だ。

Sub MetaDebugPrint()
    Debug.Print "Debug.Print """ & Replace(inputbox(""), """", """""") & """"
End Sub

実行するとインプットボックスが表示されるので、文字列化したいコードを入力する。
f:id:t-hom:20151121101613p:plain

図の例では、イミディエイトウインドウに次のように出力される。

Debug.Print "Debug.Print ""T:T"", True And True"

これでコードを文字列としてDebug.Printするコードが出来上がったので、あとは以下のように演算子を文字列の外に出して変数に加工すれば、今回書いたようなコードが出来上がる。

Debug.Print "Debug.Print ""T:T"", True " & x & " True"

メタプログラミングでは以下の記事でもいくつか取り扱っているので、興味のある方はどうぞ。

thom.hateblo.jp

thom.hateblo.jp

thom.hateblo.jp

thom.hateblo.jp

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。