通常の関数は y = f(x) のように、ある値を与えると、関数内で処理をして値を返します。 TJSの関数でも、値を与え、それを返すという関数を作る事が出来ます。

値を与える事が出来れば、関数内で外の変数名等を知る必要がなくなり、より汎用的な機能をもてますね。 今回は汎用的な関数を作る事を第一の目的として話を進めます。

汎用的な関数作り

これまでの関数は、ある目的の処理を一つにまとめる、という作り方をしてきました。 大規模なプログラムを関数で分割することで、不具合の発見が早くなり、 プログラム全体として見やすくなりました。

ただし、その関数が多くの場所で使われるものだとすれば、 これまで以上に汎用性に富んだ作り方をしなければなりません。

なぜなら、処理の内容を変えるには関数の処理そのものを変えなくてはならないからです。 関数値を返すことによって、その関数の汎用性は高まりましたが、 計算する変数や表示する文字までは変えられません。

計算する変数を変えたければ、また違う関数を作る必要があるのです。例えば次のように。

//変数aとbを加算して返す
function func1 ()
{
    return a + b;
}

//変数cとdを加算して返す
function func2 ()
{
    return c + d;
}
				

関数の基本は必要最小限の機能を持たせる事だと書いてきました。 これらの関数にとって必要最小限の機能とは何でしょうか?「ある変数同士を加算する事」です。

しかし、このままでは変数の名前に大きな制限が出来てしまいます。 後になって変数cとdに変えたい場合や、そもそも関数の外で変数aとbが使われていないと、関数func1は全く役に立ちません。 すなわち、根本的にプログラムを見直す手間が生じます。

では、計算する値そのものを、状況に応じて変えるにはどうしたらいいのか? という説明を次の項目で行います。

値を渡す

早速、結論から入ってしまいましょう。 関数を呼び出す際、次のように呼び出すと関数に任意の値を渡す事が出来ます。

関数名 ( 渡す値 );

渡された値を受け取るには、関数部分に次のように書きます。

function 関数名 ( 受け取る値 )
{
    //処理内容
}
				

受け取る値、には「その値を受け取る変数名」を書きます。 変数の命名規則に則っていれば、何でも構いません。 変数で値を受け取ると、その関数内で自由に変数を使えます。 当然、その変数には呼び出す際に指定した値が入っています。

値を受け取る関数

では早速、ある値を受け取って、それを表示する関数を作りましょう。

function showMes ( message )
{
    System.inform( message );
}
showMes("表示メッセージ");
				

showMes("表示メッセージ");と呼び出すことにより、 関数showMesで受け取った変数messageに表示メッセージという文字列が入りました。 お分かりの通り、この関数は「System.inform」を「showMes」という別名で呼び出すための関数として機能します。 (とはいえ、この関数はSystem.informの別名関数としては未完成です。)

この時、関数に渡される値を引数(ひきすう)と呼びます

また、呼び出し側の引数を実引数、受け取り側の引数を仮引数と呼んでいます。

上の例では、関数showMesの「message」が仮引数で、 showMes("表示メッセージ");で呼び出した「表示メッセージ」が実引数です。

値を渡す側……実際に存在する値を渡すから実引数

値を貰う側……一時的に変数で値を受け取るから仮引数

引数の複数指定

引数は、複数指定する事も出来ます。 引数を複数指定する時はコンマ(,)で区切って指定します。

関数名(引数1,引数2,引数3...);

当然、受け取る関数にも同じ数の仮引数を指定しないと値が受け取れません。

function 関数名( 仮引数1,仮引数2,仮引数3...)

引数1=仮引数1、引数2=仮引数2、というように、 呼び出し側の引数の順序と、受け取り側の引数の順序が対応します。

最も左側に指定された引数を第一引数と呼びます。 同様に、二番目の引数は第二引数、三番目は第三引数と呼びます。

第二引数が…と来れば、関数に指定された引数のうち、左から二番目の引数と覚えましょう。

これで、ある値とある値を加算し、その値を返すという関数が作れますので、 以下にその例を示します。

function sum1 ( num1 , num2 )
{
    return num1 + num2;
}
System.inform("5+20="+sum1(5,20)+"です。");
System.inform("9+50="+sum1(9,50)+"です。");
				

関数sum1は、引数を二つ受け取り、二つの値を加算して返す関数です。 関数の意図である「二つの値の加算結果を返す」という処理に徹しています。 ですから、他の箇所でも使える状況が出てくるでしょう。

C/C++ 関数のオーバーロード

TJSではオーバーロードは無意味です。 そもそもオーバーロードは引数のデータ型互換性にかかわる問題なので、 データ型を考慮する必要の無いTJSには、意味の無い機能なんですね。

でも、一応多重定義する事は可能です。 その場合、最後に定義した同名関数が実際の関数として機能します。 (最後とは、最も下に書かれた関数という意味です)

System.inform(sum1(5,20));

function sum1 ( num1 , num2 )
{
    return num1 + num2;
}

//こちらが機能する関数
function sum1 ( num1 , num2 )
{
    return num1 * num2;
}
					

この例では、sum1の実体は最後の関数なので、 「num1*num2」を返す関数として機能します

デフォルト引数

前の項で、二つの値を加算して返す関数sum1を作りました。 5+6の計算結果を得たければ次のように呼び出せばよいのでしたね。

sum1(5,6);

この時、関数sum1は様々な箇所で使われているとして、 大抵の場合は、5+6の計算結果だけを得たいとします。 しかし、特定の場所だけは3+2のような計算をさせたいとします。

この場合、何度も同じ数値を入力しなければならないので手間です。

sum1(5,6);
sum1(5,6);
sum1(2,3); //これだけは2+3にしたい
sum1(5,6);
sum1(5,6);
...
				

このような場合、予め受け取る引数に値を代入しておく事が出来ます。 やり方は簡単で、通常の変数の代入のように、仮引数の後に記号「=」を使って値を指定します。

function sum1 ( num1=5,num2=6 )
{
    return num1 + num2;
}
System.inform(sum1());
				

これを実行すると、呼び出し側には何も指定していないにもかかわらず、 「11」という結果が表示されました。 「num1=5,num2=6」によって、それぞれの値が予め代入されているからです。

この時、予め値が指定された引数をデフォルト引数と呼びます。

デフォルト引数は、引数に何も指定されなかった場合にのみ代入されます。 したがって、値を指定して呼び出せば、各引数はその値が代入されます。

function sum1 ( num1=5,num2=6 )
{
    return num1 + num2;
}

//5+6の結果
System.inform(sum1());

//10+20の結果
System.inform(sum1(10,20));
				

もちろん、ある値だけをデフォルト引数にする事が出来ます。

function sum1 ( num1=5,num2 )
{
    return num1 + num2;
}
				

この場合だと、num1は5が指定されていますが、num2には指定されていません。 従って、正常な結果を得るにはnum2に値を与える必要があります。

この場合、引数1は省略するが、引数2には値を与える、という書き方が出来ます。

関数名( ,引数2);

このように、値を何も書かずにコンマ(,)だけを書き、続いて引数2を指定します。 この使い方の例は以下に示します。

function sum1 ( num1=5,num2 )
{
    return num1 + num2;
}

//5+100を計算
System.inform(sum1( ,100 )); //最初の引数省略

//5+50を計算
System.inform(sum1( ,50 ));

//200+100を計算
System.inform(sum1(200 ,100 ));
				

C/C++

TJSでは、デフォルト引数の位置に気を使う必要はありません。 ですから次のような指定も正しいのです。

function sum1(num1=5,num2)

名前のスコープ

引数を多用していると、ふと疑問が沸いてきます。 「引数の名前が関数の外にある変数の名前と同じだった場合」 問題が生じないでしょうか?例えば次のような例です。

var name="山田";

function dispName ( name )
{
    return name;
}
				

関数dispNameの引数の名前はnameです。 関数の外側では「name="山田";」と書かれています。もし、ここで

dispName("田中");

と呼び出したとしたら、変数nameの値が「田中」に変わってしまうのではないか?という不安があります。 それを確かめてみましょう。

var name="山田";

function dispName ( name )
{
    return name;
}

System.inform( dispName("田中") );
System.inform( name );
				

最初は「田中」を引数として渡しているので、田中と表示されました。 問題は次です。変数nameの値を表示してみると「山田」と表示されました。 つまり、関数の外側で「var name="山田"」として用意した変数nameには何の影響も与えていません。

これから分かるとおり「引数name」と、「関数の外側にある変数name」は全くの別物であると証明されました。 厳密に言えば、引数nameは関数dispNameの中でのみ使う事が出来る変数として動作します。 関数が終了すると、この変数は消滅します。

//このプログラムは誤り
function func1 ( name="山田" )
{
    return name;
}

//関数func1を呼び出し
func1();

//関数func1の引数「name」を表示
System.inform( name );
				

これを実行すると、「メンバnameが見つかりません」というエラーが表示されたはずです。 よって「関数func1の引数であるname」は関数の外側では参照出来ない!という事が分かりました。

引数の範囲

変数に限らず、TJSの名前には参照出来る範囲が存在するのです。(名前というと変数名や関数名などを指します) この範囲を名前のスコープと呼びます。

プログラムでは、名前が「どこからどこまで参照出来るのか?」という事を知るのはとても大切な事です。 次回は、スコープについてもう少し掘り下げて考えます。

スコープの範囲

名前が参照出来なくなる範囲とは何なのでしょう。 言い換えるなら、変数が局所的にしか使えない範囲とは何でしょう。 結論を先に言うと{ }の内部で変数が宣言されると、 その変数は{ }の内部でしか使えない変数になります。

ブロック{ }はこれまで何度も使ってきました。if文やswitch文、while文でもそうですね。 これまで使ってきませんでしたが、このブロック{}内で変数を宣言してしまうと、{}を抜けるとその変数は使えなくなります。 それを確かめてみましょう。

var num1 = 100;
if ( num1 )
{
    var num2 = 200;
    System.inform("num2の値は"+num2+"です。by if文");
}
System.inform("num1の値は"+num1+"です。");
System.inform("num2の値は"+num2+"です。");
				

if文の外でnum1を表示すると、これは出来ましたね。num1は{}の外側で宣言されているからです。 しかし、{}の外側でnum2を表示しようとすると、やはりエラーになってしまいました。

このように、{}の内部で変数を宣言すると、 その変数の名前は{}の内部だけで有効、つまり局所的な名前になります。 このような名前の範囲をローカルスコープと呼びます。 また、この時の変数を特にローカル変数と呼びます。

逆に、全ての{}の外で変数を宣言すると、どのような位置からでも、その名前を参照する事が出来ます。 今までif文の中や関数の中から、外側の変数を参照できましたよね。 このような名前の範囲をグローバルスコープと呼び、この時の変数を特にグローバル変数と呼びます。

厳密には、TJSでグローバル変数という呼び方はしません。 グローバルオブジェクトのメンバーという言い方がより正確です。 ただ、ほとんどの言語で通用する言葉である事から、本入門でもグローバル変数と呼ぶことにします。

※本入門では細かい仕様の説明は割愛させていただきますので、 グローバルオブジェクトについての説明はしません。

var num1 = 100; //…(1)
//この位置は (1)のみ参照可能
{
    var num2 = 200; //…(2)
    //この位置は (1)(2)が参照可能
    {
        var num3 = 300; //…(3)
        //この位置は (1)(2)(3) 全て参照可能
    }
}
				
スコープ

{}はいつでも使えるの?

これまで{}はif文やwhile文などに限定して使ってきたので、 あたかも、これらの文の一部のように扱っていました。 しかし実は、{}はどの位置でも自由に使ってよいのです。

使い道としては、特定の処理に限定して変数を使いたい場合。 局所的にしか変数を使わない場面ではブロック{}で括り、その中で処理をさせる。 これで、ブロックの外側の変数に影響を一切与えずに変数を操作できます。

名前の衝突回避

最後に、スコープについて更に言及して関数の説明を一区切りとします。

名前の衝突を避ける仕組み

{}の内部で変数を宣言すると、その変数の名前は{}の内部でしか使えない、というような言い方をしてきました。 こう聞くと、まるで意味なんて無いように思えます。それどころか、デメリットしか浮かびません。

ここで発想を豊かに考えてください。 {}の内側でしか使えないという事は内部限定の変数が扱えると言う事。 つまり、{}の外側で使っている変数の名前を考慮する必要が無い!という事です。 これに何の意味があるのか?

ローカルスコープが作られる最大の恩恵を受けるのは関数です。 関数は使い回せる作りにするという事は散々言ってきた事ですが、 関数の外側で、その関数で使われている変数を参照出来ると困った事になります。 例えば次の例を見てください。

var Number1 = 1; …(1)
funcTest();
System.inform( Number1 );

function funcTest ()
{
    …処理…
    var Number1 = 2; …(2)
    …処理…
}
					

(1)の変数Numberは1が入っています。 funcTest()と呼び出すと、(2)でNumberを宣言して2が代入されています。 この時同じNumberという名前を使っているので、(1)のNumberの値が2に変わってしまうと厄介です。

もちろん、外側の変数を本当に使いたいのなら別ですが、(2)のNumberは関数funcTestの中だけで使いたいのだとしたら? 外部の変数に影響を与えてしまうと大きな不具合になります。 そうならないためには、外側で使われそうに無い変数名を付ける、という面倒な作業を必要とします。例えば次のように。

var Number1 = 1; …(1)
funcTest();
System.inform( Number1 );

function funcTest ()
{
    …処理…
    //変数の名前を変える
    var funcTestNumber1 = 2; …(2)
    …処理…
}
					

しかし、これでも確実ではありません。 関数の外側で「funcTestNumber1」という名前が使われていない、という保障はどこにも無いからです。 これでは、せっかく使い回せる関数を作っても、変数名に大きな制限が加わってしまう。

そうなると、関数を作る側は外側で使われる変数名を、 関数を使う側は、関数の内部で使われている変数名をそれぞれ考慮する必要が出てきます。これは頂けません。

こんな事態を避ける為にも、TJSにはローカルスコープという概念が備わっているのです。 大きなプログラムを作れば作るほど、その恩恵は一層高まるでしょう。

外部の名前を明示的に指定する

ローカルスコープは便利ですが、ここでまた問題があります。 外部で使われている変数Aを、ある関数の中から参照したいとします。これは普通に出来ますね。

問題はここからです。 この関数の中でも変数Aを使いたいのですが、 変数Aをこの関数の内部だけで使う変数…つまり、ローカル変数としたい場合です。例えば次の例。

var num1=1;
func();

function func()
{
    //これはローカル変数num1
    var num1=100;

    //これはローカル変数のnum1を指定
    //しかし、外側のnum1を使いたい……
    System.inform(num1);
}
					

この場合、ローカル変数の名前を変えてやる必要が出てきます。 またしても変数名に制限が出来てしまう。これはまずいです。

そこで、グローバルスコープである変数を明示的に指定する手段がTJSにはあるのです。 使い方は簡単です。

global.変数名

このようにglobalに続いてドット( . )を書き込み、 その後で外部の変数名を指定します。 こうすると、{}の内部から外側の(グローバルな)変数を明示的に指定できます。 この動作を以下のプログラムで確認してください。

var num1="グローバルスコープ";
func();

function func()
{
    //これはローカル変数num1
    var num1="ローカルスコープ";

    //これはローカル変数のnum1を指定
    System.inform( num1 );

    //これはグローバル変数のnum1を指定
    System.inform( global.num1 );
}
					

グローバル変数を指定する場合、globalは省略できます。 この場合、ドット( . )に続いて、外部の変数を指定します。

var num1="グローバル";

function func()
{
    var num1="ローカル";

    System.inform( num1 );
    System.inform( .num1 );
    //↑ドットに注目!
}
						

これは、先頭に「global」が付けられているものだ、と自動的に判断してくれるからです。 しかし、読みやすいプログラム作りを推奨する当入門としてはお勧めできません。

引数の省略

これまで使ってきた System.inform() という命令。 どうも長くて使いづらいですよね。打ち込む手間もあるし、スペルミスがあるかもしれない。 そんな時、関数を別名で作る方法があります。

方法は二つあるのですが、今回は「ある関数を作って、内部でSystem.inform()」を呼び出すという手段を使います。 ここではC処理系に習って print という関数を作りましょう。

function print (txt , cap="")
{
    System.inform(txt , cap);
}
print("こんにちは!");
				

System.informは引数を二つ受け取ります。 引数txtは表示するメッセージです。これまで表示したい文字を指定してきましたね。 capはメッセージダイアログのタイトルバーに表示されるメッセージです。(例えば、警告など。)

ここで着目して欲しいのは、関数printに渡された引数を、そのままSystem.informへ渡しているという点です。 これでも普通に動くのですが、呼び出す関数次第では次の二点が問題となる場合があります。

  • 引数の数が増えると、同じ数を指定するのが手間になる
  • txt、capという名前は、同関数内で変数として使えない。(使うとSystem.informに渡す引数が違ってしまう)

そこで、このような関数を作る場合、渡す引数を省略するという方法があります。

System.inform(...);

ここで使われている「...」(半角ドット三つ)は、 ある関数(関数Aとする)の中で関数(関数Bとする)を呼び出す際に使える便利な記号で、 関数Aに渡された引数の全てを関数Bに渡すという意味を持ちます。

引数の省略

省略によって引数の数やどのような引数を渡されたのかを知る必要がなくなります。

// print.tjs
function print (txt , cap="")
{
    System.inform(...);
}
print("こんにちは!");
				

引数の省略もう一押し!

省略記号(...)には、もう一つ利点があります。 受け取った引数を関数内で変更していても、受け取った引数そのままの値を渡す効果があります。

function func1( a,b,c )
{
    a=9999;
    func2(...);
}

function func2( a,b,c )
{
    System.inform(a+b+c);
}
func1("文字を","引数に","渡します");
						

関数func1内で引数aの内容を変更していますが、 関数func2には、func1を呼び出した時の引数の値が渡されている事を確認してください。

このように、関数内で引数の値を変更したいが、ある関数を呼び出す時は引数そのままを渡したい、という時に便利です。 関数func2の呼び出しを次のように変えると問題が起こる事を確認出来ます。

func2( a,b,c );