型名の区別

そもそもなぜ変数の型宣言が必要なのか。
一つは変数への監視を強めて想定外の数値などが混入したりを防ぐという理由がある。
しかしVBAではそれができていない。
暗黙のうちにキャスト(型変換)が行われてしまい、制御されないということだ。

どのような動作をするのか、プログラムで確認していこう。
Dim a_var As Variant, a_int As Integer, a_str As String
a_str = 123
a_var = a_str
a_int = a_str
Debug.Print a_str
Debug.Print a_var
Debug.Print a_int
このプログラムは全く問題なく動く。

ここで結果が表示されるイミディエイトをよく見るとa_str,a_varとa_intの値の表示が微妙に違う。
a_intは空白文字が1つはいる。
数値表示なので正負記号が入るところが空白表示されていると考えられる。

a_strには数値から文字列に変換されて代入される。
a_varは文字列型から代入されれば文字列型として扱う。
a_intは暗黙の了解で文字列型から数値型へ変換される。
凄いややこしいことをやっているのだが、結果としては歓迎すべき動きとは言えない。

以下のプログラムはどうか。
Dim a_var As Variant, a_int As Integer, a_str As String
a_int = "123"
a_var = a_int
a_str = a_int
Debug.Print a_str
Debug.Print a_var
Debug.Print a_int
同じく全く問題なく動く。
今度はa_strとa_intの値の表示の前に空白が入っている。
a_intには文字列から数値に変換されて代入される。
a_varは数値型から代入されれば数値型として扱う。
a_strは暗黙の了解で数値型から文字列型へ変換される。

明示的に変数の型宣言をして、明示的な定数をいれるところですら、チェックをしてくれないのである。
お話にならない。
これでは型宣言した意味がないも同然である、
ならばVariant型できちんとそれをどう扱うかを意識しながら扱った方が良い。

では以下ならどうか。
Dim a_var As Variant, a_int As Integer, a_str As String
a_str = "123a"
a_var = a_str
a_int = a_str
Debug.Print a_str
Debug.Print a_var
Debug.Print a_int
a_int=a_strで型変換エラーが出る。
変換できないのはさすがに看過しないようである。

続いて‘+’演算子の処理を見てみよう。
Dim a_var As Variant, a_int As Integer, a_str As String
a_int = 123
a_var = a_int
a_str = a_int
Debug.Print a_str + a_str
Debug.Print a_var + a_int
Debug.Print a_var + a_str
Debug.Print a_var + a_var
Debug.Print a_int + a_int
123123
246
246
246
246
Dim a_var As Variant, a_int As Integer, a_str As String
a_str = 123
a_var = a_str
a_int = a_str
Debug.Print a_str + a_str
Debug.Print a_var + a_int
Debug.Print a_var + a_str
Debug.Print a_var + a_var
Debug.Print a_int + a_int
123123
246
123123
123123
246
これらの結果から‘+’演算子の処理は次の法則になるようだ。
数値同士なら算術加算となる。
要素に文字列型と数値型が混合していると文字列が数値変換されて算術加算となる。
文字列同士なら文字列として結合される。
という2つの大きく異なった動作をする。困ったものだ。

このことと合わせて、型名を数値型か文字型と決めてしまうと却ってバグを誘引してしまうケースが出てくる。
a = "320"
Debug.Print a + "2"
Debug.Print a + 3
b = 320
Debug.Print b + "2"
Debug.Print b + 3
322
323
3202
323
これがプログラムの一部だとして結果に違和感を感じないだろうか。
上記の法則に合っていないような感じを受けてしまうからだ。
実は上の方にこういう変数定義があったからだ。
Dim a As Long, b As String
変数の管理をちゃんとやっていなかったのが問題なのだ、というのも正論である。
しかしきちんと型名宣言したが故にバグを誘引してしまった、というのも実態である。
もしこれが
Dim a As Variant, b As Variant
であったのならば
3202
323
322
323
となり、少しは妥当に思える結果になる。
「少しは」といったがこれは
Dim a As String, b As Long
とした結果とも同じであり、VBである以上はもうどうしようもないのである。

むしろVariant型を使っているんだという意識をしっかりもって明示的に数値と文字列を変換しながらやった方が良いのではないだろうか。

明示的ということで次のプログラムを見てみよう。
ここに出てくるval関数は文字列を明示的に数値に変換する関数である。
Dim a, b, c '←全てVariant
a = "320"
b = "400"
c = a + b
Debug.Print c
c = val(a) + val(b)
Debug.Print c
一つ目は文字列の連結になるので320400。
二つ目は変数a,bを数値に変換して数値加算となり720。
少しはマシに感じられるのではないだろうか。

これでも曖昧さは残るので文字列結合のための演算子である"&"を使うべきだろう。
Dim a, b, c '←全てVariant
a = "320"
b = "400"
c = a & b
Debug.Print c
c = val(a) + val(b)
Debug.Print c
この演算子は必ず文字列として結合するので
a = 320
b = 400
c = a & b
この場合でも変数の値は両方とも文字列変換されて結合される。
つまりcは320400となる。

本来はこうあるべきということで、例えば変数に型のない言語としてPerlをあげる。
Perlでは変数の名前の前に$をつけるとして見て欲しい。
以下のように記述ができる。
$a = "320";
$b = "400";
print $a + $b;
print $a.$b;
“+”は常に数値演算子としての加算を行うので結果は720である。
“.”は文字列として結合する演算子であり結果は320400となる。
実に明快であり、混乱が少ない。
+演算子の定義については、BASICとしての互換に拘ったのかもしれないが、結果としてひどい事態を招いている。
とはいっても取り返しはつかないので、Variant型を使うべし、という作法を薦めるわけである。

Variant型は良くない、使うべきではないという論旨の話もよく見かける。
Variantのような変数は別にVBだけではなくて、他の言語ではよくある話である。
数値と文字列を意識せずに使える変数というのはより高級な言語に実装されるものであり、近代的なはずなのである。
しかしそれにあわせて演算子を含めて言語仕様を注意深く定義しないと、バグの温床になるのである。
その点でVBAは実に“残念な”言語になってしまったと言える。

そうはいっても使用者である我々ができることは気をつけるしかない。
こういう言語仕様だと言うことを認識して注意深く使っていくしかない。
comments (0)

コメント

Comment Form