関数の基本
いよいよ、プログラムが面白く(?)なってくる話題に触れる時がやってきました。関数です。
関数、と聞くと数学を思い出す方が多いと思うのですが、TJSでいう関数は、 簡潔に言ってしまえば複数の処理の集合体です。 これまでメッセージを表示したり、計算したり、様々な処理を見てきましたが、 これらの処理を集め、いつでも一括処理できるように準備した集合が関数の特徴の一つです。
//1×5の計算をし、結果を表示する処理をまとめる 関数 () { var sum = 1*5; System.inform( sum ); }
関数が扱えるかどうかで、作れるアプリケーションの規模に雲泥の差が生まれますから、まさにTJSの核とも言うべき大切な処理です。 こんな事を言うとビックリするかもしれませんが、いざ使ってみると、そんなに難しい事ではありませんからご安心ください。
関数参照
まずは基本書式です。
function 関数名 () { //処理 }
このように、functionに続いて関数に名前を付けます。 名前は変数の命名規則と全く同じ付け方が出来ます。 例えば、次のように。
function funcHello ()
{
System.inform("こんにちは!");
}
ここでは、関数の名前を「funcHello」にしました。 これは
System.inform("こんにちは!");
を実行するだけの関数です。 これまでのif文やwhile文と同様に、処理内容全体を{ }で囲みます。
関数って何か変?
普通、関数は何かを入れたら何かの値を返してくるはずです。 しかし、TJSの関数は何も返さなくても、立派な関数なのです。 (何かを返す関数、は後で説明しますね)
では、サンプルプログラムを実行してみてください。 ……何も起こらなかった?それが正常です。
関数は、ただ処理内容を書いただけでは実行されないのです。 そう、関数を実際に使うには呼び出す必要があるのです。
呼び出すには次のように書きます。
関数名();
この書き方を関数参照と呼びます。 関数参照は「式」なので、どこにでも書けます。例えば次のように。
System.inform( 関数名() );
では、次のプログラムを実行してみてください。
funcHello();
funcHello();
function funcHello ()
{
System.inform("こんにちは!");
}
たった一行「funcHello();」と書くだけで「こんにちは!」が二度表示されました。 ある処理郡を関数としてまとめ、後からその関数を呼び出すだけで何度でも同じ処理を繰り返して行う事が出来る。 これこそが、TJSで関数を使う最大のメリットなのです。 (このように、値を返さないような関数はサブルーチンとも呼ばれます)
繰り返し処理と言えば、forやwhile文が思い浮かぶかもしれませんが、これらは対応する{ }内に用途が限定されています。 関数はどのような場面、場所でも繰り返し処理が行える。 単純なようで、関数は反復文とは別格の機能を持っているのです。
C/C++関数との三つの違い
(1)関数値の型指定が無い。
変数の時もそうでしたが、TJSでは型を自動的に変換してくれるので、
明示的にデータ型を指定する必要がありません。
例えば次の書き方は必要ありません。
int function func1(){……}
(2)実行順序を気にしなくて良い。
サンプルプログラムを見て頂ければお分かりの通り、
関数を定義する前に、定義前の関数を呼び出しています。
TJSでは関数を並べる順番にかかわらず、どの位置からも関数を呼び出すことが出来ます。
(3)プロトタイプも必要ない。
(2)の理由から、当然プロトタイプという概念も必要ありません。
これが気持ち悪く感じる方は、最初に関数を定義してから呼び出す、という形を取ってください。
プロトタイプを省略する形と同じ書き方にするという考え方です。
処理をコンパクトにする
関数を使うメリットの二つ目として、複雑な処理をコンパクト…つまり省力化出来るという点が挙げられます。 例えば、同じような処理が何度も出てくるようなプログラムでは、 その都度同じような文を書き連ねたのでは効率も見た目も悪いですね。
var a=10; var b=5; // 変数aにbを足した結果を表示。 a += b; System.inform("変数aの値は"+a+"です。"); System.inform("a×b="+(a*b)); // 変数aにbを足した結果を表示。 a += b; System.inform("変数aの値は"+a+"です。"); System.inform("a-b="+(a-b)); // 変数aにbを足した結果を表示。 a += b; System.inform("変数aの値は"+a+"です。"); System.inform("a/b="+(a/b));
これは、変数aに変数bの値を足し、 その都度、四則演算をさせた結果表示を行う、というプログラムですが、ある共通点がありますね。
// 変数aにbを足した結果を表示。
a += b;
System.inform("変数aの値は"+a+"です。");
この部分が三箇所もあります。 このように、同じ処理が何度も登場し、それも何度も使う必要がある場合、その部分を関数にすると便利です。
var a=10; var b=5; aPlus(); System.inform("a×b="+(a*b)); aPlus(); System.inform("a-b="+(a-b)); aPlus(); System.inform("a/b="+(a/b)); function aPlus() { // 変数aにbを足した結果を表示。 a += b; System.inform("変数aの値は"+a+"です。"); }
関数「aPlus」は、変数aの値に変数bの値を足し、結果を表示するという処理をします。 各所で「aPlus();」と呼ぶだけで、この機能を使う事が出来ました。 本来ならばaPlus();と書かれている場所に、処理を書く必要がありますから、 手間であると同時に、タイプミスもありえます。例えば
a -= b;
とでも書いてしまった場合、不具合になります。 つまり、意図しない結果が表示されてしまいます。 一方、aPlus();という記述ならば、そうそう間違う事は無いでしょうし、 この場合のタイプミスは「エラー」として知らせてくれるので、すぐに気づけます。
処理を分割する
関数を使う利点は他にもあります。 例えば、次のような処理があるとしましょう。
var a = 10; var b = 60; var c; // 変数の計算部分 : : c = a * b; : // 変数の表示部分 System.inform("変数cの値は"+a+"です。"); System.inform("変数cの値は"+b+"です。"); System.inform("変数cの値は"+c+"です。"); // 全く別の処理 処理1… 処理2… : : 処理100 :
無駄に縦長になるプログラムですが、よく見ると一定の処理で分割できる部分がありますね。 「変数の計算」「変数の表示」「別の処理」という三つの区分が出来上がります。 そこで、それぞれを関数にするのです。
var a = 10; var b = 60; var c; function calcVar() { //変数計算 } function showVar() { //変数表示 } function etcFunc() { //その他処理 } calcVar(); showVar(); etcFunc();
このように処理を関数で管理する事で、不具合が発見しやすいという大きな利点が生まれます。 文法上のエラーならば吉里吉里がエラーが起きた箇所を教えてくれます。 ですが、文法上は正しくても仕様上間違っているエラーに関しては、実行自体は出来るので自力で箇所を探さなければなりません。 (仕様上の誤りとは意図した処理をしてくれない、目に見えないエラーを指し、プログラムで最も厄介な誤りです。)
…変数計算時に誤った演算子(+や*)を使っているのか?
…その他の処理部分で誤った処理があるのか?
…そもそも全く別の変数を表示させているのか?
そんな時、数百…または千単位のプログラムの場合に、一行ずつ誤りを探していては日が暮れてしまいます。 処理を関数で管理しておけば、怪しい処理をしていそうな関数だけをチェックすれば済むのです。
var a = 10; var b = 60; function calcVar() { //処理を変えるのはこの関数だけ。 // 変数aにbを掛けた結果を表示。 a *= b; // ←ここが「*」ではなく「+」になってた! } System.inform("変数aの値は"+a+"です。"); :
プログラムは読みやすく、それでいて出来るだけ楽をして書くのが望ましいのです。
これまで紹介したサンプル関数のような使い方は、好ましくありません。 何故ダメなのか?は、次第に分かってくると思いますので、 ここでは、使い方の一例として記憶しておいてください
何かを返す関数
通常、関数には必要最小限の意味を持たせる事が基本です。 例えば、前回「変数aにbを掛けた結果を表示」という関数を作りましたが、 この関数で行いたい事(関数の本質)は「変数aに変数bを掛ける」事ですから、 変数の表示自体は別の場所で行うのが望ましいのです。
何故なら、アプリケーションによっては関数の結果を表示するのではなく、 印刷する場面があったり、テキストデータとして出力する場面がありますよね。 もしかしたら、クリップボードにコピーしたいだけかもしれない。 そのたびに結果を表示されたら、ちょっと煩わしい。
とはいえ、その度に違う関数を作るのはとても効率が悪いです。
var a = 10; var b; function showVar() { b = a + 100; //変数aの表示 } function copyVar() { b = a + 100; //変数aをクリップボードへコピー } function printVar() { b = a + 100; //変数aの値を印刷 }
これを解決する為には、関数から計算結果を値として返すという手法を取ります。 関数が何かを返すという事は、結果的に関数自体がその値そのものを持っている!とも言えます。 このように関数が持っている値を関数値と呼んでいます。(関数が返す値と同一です)
また、関数が返す値を特に戻り値、または返り値と呼ぶ事もあります。
専門用語に大混乱……!
実は関数値も戻り値も返り値も、全て同じ値を指す表現です。 C処理系では関数値、Java系では戻り値、CGI系では返り値という用語が一般的なマニュアルに書かれています。 人によって使う用語が変わる、というのはよくある事ですから、用語自体にほとんど意味は無いという良い例です。
これまで、無理に用語を覚える必要が無い、と言ってきたのには、こういう理由もあるんです。 ご自分で覚えやすいように覚えれば、それで良いのです。 小難しい言葉を無理に覚えて、混乱してしまったのでは意味が無いのだから……。
function 関数名 () { //処理内容 return 式; //式の返す値が関数値 }
関数内で、上記のようにreturnに続いて式を書くと、関数値として返す事が出来ます。 (原理としては、例えば「1 + 2」という式は「3」を返しますよね。これと同じです。)
var a = 10; function showVar () { a += 100; return a; // aを返す }
returnにはもう一つの意味があって、returnが関数内に書かれると、その関数を強制終了します。 ですから、次のように書く事も出来ます。
function sum () { System.inform("関数sumが呼ばれました。"); return; System.inform("この処理は行われません。"); } //関数の呼び出し sum();
途中でreturnが登場したので、関数が終了しました。 よって、最後の「この処理は行われません。」という表示はしません。 この時、returnの後に何も書かれていませんので、何も返す事はありません。 関数値は省略出来る、という事は覚えておきましょう。
関数値を利用した処理
関数が返す値…関数値を使用して、変数xの二乗の値を返す関数を作ってみます
var x=5; function calcX () { return (x*x); } //関数calcXが返した値をそのまま表示 System.inform( calcX() );
calcX()は式なので、このように指定する事も出来ます。 式は「値を持つもの」という説明は何度かしました。 関数calcXは値を返すので値を持っている。だから「calcX()」は式なんですね。
何故このように関数の役割を限定するのかというと、関数を使い回すためです。 使い回しとは、様々な場面で同じ関数を使う事です。
変数xの二乗値をどのように使うか、は使用する場面によって異なる事がありますね。 表示、演算、if文の条件式…など。
var x=5; function calcX () { //変数xの二乗値を返す return (x*x); } //関数値を変数yで受け取る var y = calcX(); if (y > 25) { System.inform("変数xの二乗値は25より大きいです"); } else { System.inform("変数xの二乗値は25より大きくないです"); } System.inform("変数xの二乗値は"+y+"でした。");
このような関数の作り方をしていると、変数y(つまり変数xの二乗値)をどのように使うかは自由です。 よって、関数calcXは変数xの二乗を返す関数として、様々な用途で使える事でしょう。
日常生活で楽をする、と言えばマイナスの印象がありますが、プログラムの世界で楽をするという考え方はとても大事な事。 楽をすればするほど、後から修正する手間も省け、不具合の少ない優れたプログラムとなり得るのだから。