2022-01-14 VBA 関数の仮引数はどうあるべきか。暗黙の型変換をどうやって回避するか

1
ほえほえ@スプシマン @hoehoe1234

この辺も結構悩んでいた時期かな?VBAは暗黙の型変換と「そうみえるものはそうあつかう」という特性により、バリアントでの関数IFが一番「変換がおきない」のでいいんだけど、その代わりに時として関数ないで引数の型変換を明示的する必要がおきるんですよね。 paper.dropbox.com/doc/VBA--BZ~76…

2022-01-14 00:48:15
ほえほえ@スプシマン @hoehoe1234

それでも暗黙の型変換に比べれば、そのコストは正当化されるとおもいます。num_add(a_val1, a_val2) なら内部で num_add = Cdbl(a_val11) + Cdbl(a_val2) とすれば良いだけで。型をLongにしても間違いが防げるわけでもないんですよね。この当たりは悩ましいですね。正しく理解するのが一番なんですけど

2022-01-14 00:50:38
ほえほえ@スプシマン @hoehoe1234

VBAではそれが当然のようには「求められない」というのが実情なので。であればしかたなしに具象型の仮引数にするほうが確かに間違いは少なくなるでしょう。多様性を利用したプログラミングは楽しいのですがそういうのを求めるのはちょっと無理筋のところがありますから。

2022-01-14 00:52:12
ほえほえ@スプシマン @hoehoe1234

逆に、自分一人で使うライブラリなどでは挙動をすべて把握できるのでどんどん、バリアント型仮引数を使って良いと思います。結局はスキル依存なんで、「どこに基準を置くか」でいえばVBAはその特性から高い基準を求めるのは無理があるんですね。

2022-01-14 00:53:29
ほえほえ@スプシマン @hoehoe1234

典型的な sum(a_va1, a_val2) で、文字列が来たときに sum(1, "2")なんかだと sum側でCdblすればよいだけの話で、Longで受けても"2"が2として渡って来ちゃうんですね。こんな感じです。 pic.twitter.com/vUAIpLo1nA

2022-01-14 00:58:15
拡大
ほえほえ@スプシマン @hoehoe1234

これは時として(仕様により)、バリアントより悪い結果なんですよね。どちらにしろエラーが検出できていません。具象型で受けると引数が暗黙の変換されちゃうんで引数エラーを検出できないんですね。これはバリアントで受けるよりも悪い結果となる場合もあります。

2022-01-14 00:59:46
ほえほえ@スプシマン @hoehoe1234

①バリアントで受ければ、めんどくさいけどきちんとエラー処理ができる。 ②byval具象型で受けると内部エラーは起きないが引数の間違いを検出できない場合がある。 このケースなんかですね。相当悪い結果です。 pic.twitter.com/zWdfYmFqjC

2022-01-14 01:01:41
拡大
ほえほえ@スプシマン @hoehoe1234

CとかJavaなどのコンパイル言語ではこのような馬鹿げた変換はされません。明示的に指定しないと精度の落ちない方向にしか(拡張方向でしか)変換されません。なので問題なんですね。VBAのByVal具象型も今見たように暗黙の変換という問題が大きいんですね。結局わかって使うしかないと思います。

2022-01-14 01:03:52
ほえほえ@スプシマン @hoehoe1234

呼び出し側が間違って my_sum(1, "3.5") と呼び出してしまうと予期せぬ結果になりますね。設計によりますが、本来は引数エラーで止まるべきでしょう(引数が間違えているので)。ここで呼び出し側が必ずLongを使ってくれるという前提はライブラリ側でせざるを得ないとうことです。

2022-01-14 01:06:43
ほえほえ@スプシマン @hoehoe1234

エラー検知の精度が大幅にさがるので、 これはmy_sum(a_val1, a_val2)のようにバリアント型参照仮引数で受けたよりも悪い結果と見ることもできます。ですから図のようなケースと大差ないということです。 pic.twitter.com/V55lefhmud

2022-01-14 01:09:58
拡大
ほえほえ@スプシマン @hoehoe1234

であれば、型チェックを行えるバリアント型参照仮引数のほうがよいのではないか?ということです。具象型ByVal仮引数も暗黙の変換より変な挙動をしますし、かつ、それが検出できないですね。結局、理解するしかなということかもしれませんね。

2022-01-14 01:11:14
ほえほえ@スプシマン @hoehoe1234

具象型仮引数の問題は、その関数自体に問題は生じにくいのですが、呼び出し側が間違った場合に「ゴミが入ったらゴミが出る」ってことなんですよね。これは一応許容の範囲内ですができれば呼び出し側の間違いをチェックしてあげたいところです。

2022-01-14 01:20:26
ほえほえ@スプシマン @hoehoe1234

反対にバリアント参照型仮引数の場合は、暗黙の挙動を理解してないと関数内部で間違いが生じることがあります。その代わりに実引数が型と共に正しく渡ってくるので共通関数側でチェックが可能になります。どちらを選ぶかはスキルレベル、目的、仕事環境で決まると思います。どちらも問題ありますから。

2022-01-14 01:22:50
ほえほえ@スプシマン @hoehoe1234

プラウガー氏的に言えば、 ①具象型仮引数は「ゴミが入ったらゴミがでる」可能性があり、②バリアント型参照仮引数は「ゴミが入ったら診断がでる」となり、後者のほうが望ましいのは言うまでもありません。しかし、一定のスキルが必要なので前者が望ましい場合のほうが多いでしょう。

2022-01-14 01:30:42
ほえほえ@スプシマン @hoehoe1234

VBAでは、仮引数に具象型を使っていると「ゴミが入ったらゴミが出る」を仕組み的に回避できない。これは呼ばれる関数側の問題とは言えないができれば避けたいところ。「ゴミが入ったら診断が出る」ほうがよい。みなさんどうやって回避してるのかな?それともレアケース?

2022-01-14 12:17:13
ほえほえ@スプシマン @hoehoe1234

1 + "2" -> "12"になるような関数は書くほうが悪いんだけど、同様に具象型引数だと1 + "3.5" -> 5とかになってしまいます。この事実から具象型仮引数には特に優位性がない、むしろ悪いと考えています。関数内だけ見ればただしですが、引数の暗黙の変換を「自分のせいではない」としているからです。

2022-01-14 12:18:44
ほえほえ@スプシマン @hoehoe1234

診断を出すには、バリアント型参照仮引数で受けて内部で数字かどうかを確認することでしょう。これには手間がかかりますがVBAの暗黙の変換を回避する唯一の手段だと思います。計算に正しさが求められる(お金の計算とか)なら後者が正しいと思います。

2022-01-14 12:20:08
ほえほえ@スプシマン @hoehoe1234

具象型仮引数にすれば誤演算が避けられると思っているのは間違いです。間違いは実引数から仮引数への変換時に暗黙に行われます。誤演算を避ける意図があるならここまで考えるべきでしょう。逆に殆どのケースではそういう事は「どうでもいい」わけです。お金絡みの人は勉強する必要があるということです

2022-01-14 12:21:57
ほえほえ@スプシマン @hoehoe1234

言いたいのは「お金がらみ」の大切な計算をおこなうのであれば、関数は正しく書きましょう。ということです。具象型仮引数にすることではこの事象は回避できません。呼ばれる関数は正しく動いているつもりでも呼び出し側を含めて考えると障害の発生するポイントになるからです。

2022-01-14 12:23:52
ほえほえ@スプシマン @hoehoe1234

具象型が安全という神話は信じないほうが良いです。正しく計算するには「できるだけ暗黙の変換避ける」とう観点が必要になります。簡単に言うと具象型仮引数は安全でもなんでもないんです。もちろん、ここまで考える必要のないことがほとんどです。

2022-01-14 12:25:11
ほえほえ@スプシマン @hoehoe1234

こんな感じでまとめて・・・。まではサイトをみればわかるので、その後に、滝libとして抽象化したデータ型判定関数を作成するよてい。 tk_is_real_numberとかtk_is_int_numberとかつくって、その上はtk_is_numberみたいな。数の系統樹に従ってつくるけどそこにVBAらしい便利さを盛り込む予定。 pic.twitter.com/LRlGW0WL9T

2022-01-15 12:43:27
拡大
ほえほえ@スプシマン @hoehoe1234

VBAをクリーンに作るには、暗黙の型変換を利用しつつ、最大限に暗黙の型変換を避けること。1つの関数中では最大限に暗黙の型変換を利用し、かつ、関数のIF(引数など)ではできるだけ暗黙の型変換を回避する必要がある。暗黙の型変換は便利さの裏返しで精度ががばがばなので障害の温床。

2022-01-15 12:47:51
ほえほえ@スプシマン @hoehoe1234

有名な、1 + "2" -> "12"なんてのは 引数のかチェックのあと 合計値を求める = CDbl(a_var1) + CDbl(a_var2) ってしとけばいいだけです。必要があれば事前に引数が本来の数字かどうかチェックできます(isNumberは使えない)。これは具象型引数にはできないことです。

2022-01-18 13:26:56
ほえほえ@スプシマン @hoehoe1234

言葉を変えれば、バリアントを使うことにより「暗黙の型変換」に介入できるんですね。具象型仮引数ではゴミが入ったらゴミを出さざるを得ませんが、バリアント型仮引数では診断をだせます。これは使う側のミスを防ぐ機会が増えますので望ましいことです。VBAの暗黙の型変換はそれほど他言語と違い

2022-01-18 13:28:27
ほえほえ@スプシマン @hoehoe1234

ますし、障害の温床になっているんだと思います。もちろん、数値計算などでなければ関係ありません。ただ、バリアントに欠点があるという指摘は間違っているということを言いたいだけです。具象型仮引数の欠点のほうが大きいと言えます。もちろん理解が必要なので通常は具象型仮引数でよいでしょう。

2022-01-18 13:29:40