t-hom’s diary

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

VBA 関数の副作用と参照透過性について

関数とは何か

まずプログラミングにおける関数とは何かということを、はっきりさせておきたい。

私は関数を「外部から何かを受け取り、加工して返すもの」と認識している。

たとえば平方根を返す関数Sqrなどは、2を受け取るとルート2の値(1.4142135623731)を返す。
f:id:t-hom:20150913160550p:plain

しかし、Microsoftのヘルプには、この定義に当てはまらないものもある。
たとえばNow関数である。

Now関数は、FunctionではなくPropertyとして定義されている。
thom.hateblo.jp

そして、Now関数は値を受け取らず、ただ日時を返すのみである。

まあ、「無」を受け取ると考えれば定義には合致する。
f:id:t-hom:20150913161001p:plain

とにかく関数は、値を返すものである。これに例外は無い。

MsgBox関数はどうか。

Sub Test()
    MsgBox "Hello, VBA"
End Sub

何も返していないように見えるが、実はMsgBox関数は1を返している。
(厳密にはVbMsgBoxResult列挙型のvbOK定数値が1ということ)
Testプロシージャが帰ってきた値を使っていないだけで、MsgBox関数自体はちゃんと値を返しているのだ。

次のように書き換えれば、意味が分かるだろう。

Sub Test()
    Debug.Print MsgBox("Hello, VBA")
End Sub

関数の副作用とは

関数の主作用は、値を返すことである。
MsgBoxなどの表示を主目的とする関数であっても、「関数」という定義に基づけば値を返すことが主作用なのである。

ではMsgBoxの副作用とは何かというと、メッセージを表示することである。

副作用というと薬の副作用を連想して悪いものをイメージしてしまうが、プログラミングにおいて必ずしも悪いものではない。

しかし、副作用が思わぬバグをもたらすこともある。
たとえば、以下のような関数を作成してみた。

Function CellTest(x, y)
    Range("a1").Value = x
    Range("a2").Value = y
    CellTest = Range("a1").Value * Range("a2").Value
End Function

xとyを受け取り、セルに書き込んだ上で掛け合わせた値を返す関数である。
セルへの書き込みは副作用である。
もしシートにロックがかかっていたら?ActiveSheetがグラフシートだったら?
他にも色々弊害が考えられる。上記のコードは、あまり良いコードとは言えない。

関数の参照透過性とは

参照透過性とは、外部状態に影響を受けず、与えられた値のみによって結果が決まる性質のことをいう。

たとえばNow関数は参照透過ではない。
外部環境であるシステム時刻によって、返す値が変わるためだ。
f:id:t-hom:20150913163700p:plain

一方、平方根を返すSqrは参照透過である。
誰が、いつ、どのような状況で呼び出そうとも、2を与えれば必ず1.4142135623731を返す。

透過というのは、文字通り、透けて見えるということである。

内部のごちゃごちゃした計算が透けて見える。
たとえば、Sqr関数は平方根を返すということだけ知っていれば、内部の計算はまったく意識しなくて良いということである。
f:id:t-hom:20150913164936p:plain

まとめ

本来関数とは外部の状況に左右されず、与えられた値のみによって結果が決まるものである。
つまり、副作用が無く、参照透過な関数こそが、純粋な関数といえる。
そうした関数は、内部の処理を意識しなくて良いので非常に使いやすい。

自分でFunctionプロシージャーを作るときも、なるべく純粋な関数を意識すると良い。
外部環境に依存するような処理はSubプロシージャで行い、関数内では与えられた引数のみで完結させるのが良い設計である。

ただし、InputBox関数、Now関数、MsgBox関数のように、副作用の恩恵によって成り立つ関数は多数ある。
私が伝えいのは「闇雲に副作用を作るべきではない」ということであり、時と場合によっては、あえて参照透過性を犠牲にして副作用を作ることもある。
要は、こうした基本を踏まえたうえでコーディングすると良いということ。

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