過去記事で、すべての行はステートメントであり、省略せずに書くとLetやCallが付くことを説明した。
Letについてはプロパティプロシージャでの使用が大半で、代入にわざわざ書いている人はあまり見たことが無い。
Subプロシージャ呼び出しの場合、私はよくCallキーワードを使用するが、これも省略しても問題ない。
ではなぜ、在っても無くても変わらないようなキーワードが存在するのか。
理由のひとつは、Visual Basicの元になったBASIC言語から引き継いだものだからということだという説明をどこかで読んだ。
BASICからのVisual Basicにスムーズに移行してもらうために必要なことだったのかもしれない。
ではなぜ、BASICにLetが必要だったか。
これも当事者ではないので断言はできないが、Letがあったほうが、コンピューターによる文法解析のコストが低いからだと聞いたことがある。
私も以前、VBAでVBAインタープリターを作ろうとしたことがあるので、この説はうなずける。
変数、プロシージャ、定数などにつける名前を「識別子」と呼ぶが、ステートメントが識別子から始まると、変数代入なのかプロシージャ呼び出しなのか判別が難しいのだ。
もちろん人間が見たら分かる。
でもプログラムでステートメントを解析する際、他のステートメント(IfやFor、Subなど)は先頭だけ見ればそれがどの種類の操作なのか分かるのに対し、識別子が先頭に来た場合はそのために例外的な判定を作成しないといけない。
たとえば、Somethingという識別子があったとき、パターンは以下の4つが考えられる。
Something 1, 2 'Somethingはプロシージャである。 Something = 1 'Somethingは変数である。 Something(a) = 1 'Somethingは配列である。 Something.Value = 1 'Somethingはオブジェクトである。
何のステートメントなのか、プログラムで判別するには、変数リストとプロシージャリスト、配列リストを作成してどこにあるか探すという処理をするか、あるいは識別子の後にくる文字で判別するかという面倒な処理になる。
LetやCallがあれば、少なくともステートメントが代入なのか呼び出しなのかの判別は簡単になる。
Call Something(1, 2) 'Somethingはプロシージャである。 Let Something = 1 'Somethingは変数である。 Let Something(a) = 1 'Somethingは配列である。 Let Something.Value = 1 'Somethingはオブジェクトである。
これが当時Letが導入された経緯かと思う。
VBAでオブジェクト変数への代入時に、Setキーワードが必須になる。これも組み込み型の代入と内部で区別するためにやむなく導入されたものであろう。VB.Netではコンパイラが進化したからか、Setキーワードが不要になっている。
さて、プロシージャ呼び出しについても、VBAでは独自のルールがある。
たとえば、引数を1つとるProcというプロシージャがあったとする。
呼び出しは、このように書くか、
Proc 引数1
あるいはこう書く
Call Proc(引数1)
こうは書けない↓
Proc(引数1)
これもVBAの文法解釈上の都合だと思う。
VBAでは配列とプロシージャの引数に同じ丸カッコを使っているため、ステートメントの先頭にProc(引数1)と書かれた場合に配列なのか関数なのか判定が困難になるというのが理由ではないだろうか。
また、こういう書き方も出来ない。
Call Proc 引数1
プロシージャ名が先頭に来ないケースで引数を渡す場合、必ずカッコが必要になる。
常にカッコ無しで呼びだせたとしたら、このように書けることになる。
Call Proc1 Func1 引数1, 引数2, 引数3
しかし上記のプログラムは、以下のように3通りに解釈できてしまう。
Call Proc1(Func1(引数1), 引数2, 引数3)) Call Proc1(Func1(引数1, 引数2), 引数3) Call Proc1(Func1(引数1, 引数2, 引数3))
Call直後のプロシージャだけカッコ不要という仕様にすることもできたと思うが、余計ややこしい。
むしろ、「引数はふつうカッコ付きで呼び出すが、Callを省くと配列との区別が難しいために、例外的にカッコ無しになる」と理解したほうが自然な気がする。
今回の記事も私の推測にすぎないので鵜呑みにするのは良くないが、ネットで探してもVB6時代の書籍をあたっても、そのあたりの情報を見つけられなかったので、一つの可能性として記しておくことにした。