関数とは
プログラミングにおいて、特定の動作を何度も記述することがよくあります。このような場合に何度も何度も同じソースコードを記述することは記述時に手間が掛かるだけでなく、動作内容を少し変更したいときにも、何箇所もソースコードを変更しなければならなくなります。しかしこのような作業の繰り返しはバグ(プログラムミス)の原因にもなります。
そこでプログラミングでは関数というものを使います。関数とは一定の動作を記述した数式を作成し、プログラム内で呼び出す仕組みです。関数には引数(パラメータ)と呼ばれる動作を特定するためのいくつかの値が変数として与えられます。関数では引数によって決まる特定の動作を実行した後、戻り値と呼ばれる一意の値を答えとして返すようにソースコードを記述します。戻り値を返さない関数もあり、そのような関数はサブルーチンと呼ばれることもあります。また、関数には動作の記述の他に、プログラム中の他の部分で使用するために数式のような形式での宣言を伴います。
<関数の宣言>
戻り値の型 関数名(引数1, 引数2, ・・・, 引数n)
宣言には上記のように関数名、引数となる変数群の宣言、戻り値の型が含まれます。ただし、戻り値がない関数(サブルーチン)の場合は戻り値の型を「void」と記述します。そして、宣言された関数はソースコードの他の部分で数式を呼び出す形式で記述されます。関数とその呼び出しは、例えば下記のように記述します。
//〜呼び出し元〜 int w = 5; int x = 10; int y = 7; int z = CalcSample(w, x, y); int z2 = CalcSample(5, 10, 7); //上記と同じ結果が得られます。 //〜関数側〜 int CalcSample(int start, int base, int count) { int answer; if ( base > 0 ) { answer = start + base * count; } else { answer = start; } return answer; }上記ではCalcSampleという関数名で、引数に3つの整数値を宣言し、引数に応じて特定の計算をした後、計算結果の整数値を戻り値として返すような関数を記述しています。そして呼び出し元では数式を書くような形で記述します。呼び出しの記述では引数に変数を指定することも、直接の値(リテラル)を記述することも可能です。また、呼び出し元の変数名と関数の引数の変数名は同じである必要はありません。むしろ引数の変数名は関数内部のプログラムの動作に合わせた変数名に、呼び出し元の変数名は呼び出し元のプログラムに合わせた変数名にというふうに変数名が異なることの方が一般的です。
数値以外の演算処理を行う関数も作成可能です。文字列演算の関数の例を下記に示します。
//〜呼び出し元〜 string a = "abc"; string b = "def"; string c = CombineStrings(a, b); string c2 = CombineStrings("abc", "def"); //上記と同じ結果が得られます。 //〜関数側〜 string CombineStrings(string data1, string data2) { string answer = date1 + data2; return answer; //return data1 + data2; と記述しても構いません。 }次は戻り値を返さない関数の例を下記に示します。下記で使われている「ref」というキーワードは「参照型」と呼ばれるもので、関数の中で変数の値を変更すると呼び出し元の変数の値も変更させることができます。
//〜呼び出し元〜 string a = "abc"; string b = "def"; SwapStrings(ref a, ref b); //〜関数側〜 string SwapStrings(ref string data1, ref string data2) { string str = data1; data1 = data2; data2 = str; }関数を用いることで、同じ処理をソースコードに何度も記述する必要がなくなり、プログラムが加工しやすくなったり、プログラム修正がしやすくなる(保守性が上る)だけではありません。別の効果として、関数名を適切に付けることによって、その処理の内容を関数名に集約することができ、その結果、ソースコードの読みやすさ(可読性)も上がります。ですので、プログラミングにおいては一連の処理を切り出して関数化を進めていくことが重要になります。このように意識してソースコードから処理を切り出して整理し、関数化していくことを「構造化」と呼びます。現在のプログラミングではさらに発展した「オブジェクト指向」という技法を用いたプログラミングが用いられていますが、その中でもプログラムを切り出していく「構造化」という考え方は必要になります。上記の例のような文字列の足し算まで関数化するのはやりすぎですが、プログラミングの際は適切な構造化を心掛けましょう。
関数の練習として、引数としてある1以上の整数値を引数として与えると1から与えられた整数値まで足し算した結果を返す「SumUp」という名前の関数を作ってみることにしましょう。方法はいくつかありますが、まずはfor文を使った関数を書いてみます。
//〜呼び出し元〜 int x = 5; int y = SumUp(x); //〜関数側〜 int SumUp(int num) { int answer = 0; for ( int i = 1; i <= num; i++ ) { answer += i; } return answer; }上記の例では呼び出し元で1から5までの和(=15)が関数の戻り値として得られます。
プログラミングにおいては答えを出す方法は一つだけではありません。上記は一番シンプルな方法ですが、別の方法として、プログラミングとして特徴的な「再帰関数」というものを使ったみましょう。再帰関数は関数の中で自分自身を呼び出せるという特徴を利用しています。下記に上記のSumUp関数と同じ結果が得られる例を示します。
//〜呼び出し元〜 int x = 5; int y = SumUp(x); //〜関数側〜 int SumUp(int num) { if ( num == 1 ) return 1; else return num + SumUp(num - 1); //関数の中で自分自身を呼び出しています。 }上記の場合、関数の中で現在の引数から1引いた値を引数として自分自身を呼び出しています。ただし、引数が1の時は自分自身を呼び出さず1を返しています。少し分かりにくいかもしれませんが、関数は下記のような動作になります。
呼び出し元 | ↓ | SumUp(5) | = | 15 | 戻り値 |
↓ | SumUp(5) = 5+SumUp(4) | ↑ | SumUp(5) = 5+10 = 15 | ||
↓ | SumUp(4) = 4+SumUp(3) | ↑ | SumUp(4) = 4+6 = 10 | ||
↓ | SumUp(3) = 3+SumUp(2) | ↑ | SumUp(3) = 3+3 = 6 | ||
↓ | SumUp(2) = 2+SumUp(1) | ↑ | SumUp(2) = 2+1 = 3 | ||
→→→ | ↑ | SumUp(1) = 1 |
再帰関数を使わなくても答えは得られますが、関数にはこういう使い方があることも知っておくと便利でしょう。