ここでは、プログラムの処理の大枠をまとめるのに便利な、main 関数を扱います。
前回は、関数を定義して、それを自分で書いたプログラム内から呼び出して使いました。 一方で、自動で呼び出される関数も存在します。
具体的には、あらかじめ決められた、特別な名前・引数を持つ関数を定義しておくと、それが特定のタイミングで、自動で呼び出されます。 そういった関数はいくつか存在します。
その典型例が、今回扱う「main関数」です。 これは、「プログラムが実行された際」に自動で呼び出される関数です。 つまり、プログラム内に main 関数を定義しておけば、必ず呼ばれます。 どのように使うかというと、通常、プログラムの処理の大枠を main 関数に記述します。
プログラムが長くなってくると、だんだん処理内容を把握するのが難しくなってきます。 そこで、処理を複数の関数に切り分けて、整理する事が有効です。そのように整理していくと、 それらの関数を呼び出す、一番外側の大枠の処理も、「何らかの関数」にまとめたくなりますよね。 それがまさに main 関数の役割です。
VCSSLでは、関数の外側でもあらゆる処理を記述できるため、別に main 関数は必須ではありません。 処理の大枠を、プログラムの頭から(関数外領域に)ベタ書きするか、それとも main 関数にまとめるかは自由です。 短いプログラムでは前者の方が便利です。 逆に、関数がたくさん登場するような、長く複雑なプログラムでは、恐らく後者の方が可読性が高まります。
なお、C言語など、いくつかの言語では、main 関数の定義が必須になっています。
VCSSLにおける main 関数の形は、2 通りあります。最も単純なのは、以下の形です:
もう一つは、以下のように文字列配列の引数を持つ形です:
後者は、コマンドラインで活用するプログラムを作りたい場合に便利です。 プログラムを vcssl コマンドで実行した際、コマンドラインで引数を指定すると、それが上の main 関数の引数 args に渡されます。 例えば:
のように実行すると、main 関数の引数 args には要素数 3 の文字列配列が渡され、その中には "aaa", "bbb", "ccc" が格納されています。
それでは、実際に main 関数を使ってみましょう。
まずは、main 関数を使用しない場合の例です:
- 実行結果 -
プログラムの頭から、処理をベタ書きしているプログラムですね。処理内容がとても短くシンプルなので、これはこれで読みやすいです。
次に、上のプログラムの「 result = a * a - b * b + 10 」の計算を関数に切り出しつつ、処理の大枠を main 関数にまとめてみましょう:
- 実行結果 -
このように、処理が関数単位で整理されたプログラムになりました。 このプログラムが実行されると、まず main 関数が自動で呼び出されます。 そして、main 関数の中の処理で、calc 関数を呼び出しています。結果、先ほどと全く同じ処理になります。
このように関数単位で整理されたプログラムでは、人間が内容を把握する際に、以下のように読み進められます:
このように、「大枠から細部へ」の流れで掘り下げながら読めるため、長く複雑なプログラムになってくると、このような形で書く方が便利です。
最後に一つだけ、「奇妙なケース」についての補足説明をしておきましょう。
以下のように、「main 関数を定義しつつも、関数外のあちこちに処理が散らばっている」というプログラムを考えます:
少し行儀が悪い書き方ですが、しかしVCSSLでは文法的に可能です。さて、上の処理は、どういう順序で処理されるでしょうか?
このような場合の処理順序を明確化するため、VCSSLの main 関数は、「全ての関数外領域の処理が終わった後に呼び出される」というルールがあります。そのため、上の答えは:
の順で実行されます。
プログラム上での見た目の順序と、実行の順序が一致しなくなるため、一見ややこしいルールに思えるかもしれません。 しかし、実用上はこのルールに基づく方が便利です。 例えば、main 関数よりも後方で宣言・初期化されている変数などを、main 関数内で使っても、初期化が済んでいる事が保証されます。 グローバル変数/定数の初期化時に、ちょっとした手続き計算を行いたい場合もよくありますが、それも安心して宣言文に続けて書けます。 そのため、このようなルールになっているのです。