プログラミング コラム

関数の学び方

分岐や反復といった制御文に慣れてくると、本格的な作品に挑戦してみたくなるでしょう。
プログラムの規模が大きくなってくると「関数」の重要性が高まってきます。

今回はスマートなプログラム設計に欠かせない関数について、少し視点を変えて考えてみました。

関数といえば、共通する処理を部品化して、プログラム内で再利用していくイメージが一般的です。
何度も同じことを書かなくて済むため、それだけでも便利に感じるかもしれません。
しかし、特に入門者が注意すべき点は「関数にしただけで安心する」ことです。
関数にすることが目的になり、関数の品質に注意が向かなくなってしまいます。

やみくもに関数を定義しても、よい関数にはなってくれません。
いかに使い勝手の良い「便利な」関数にするかは、プログラマーの腕の見せ所なのです。

では、便利な関数とはどういう関数でしょうか?
色んな視点がありますが、分かりやすいのは「使い道が多い」事ではないでしょうか?

プログラムの中で一度しか使われない関数は、使い道が多いとは言えません。
ここでも、あそこでも、プログラムのあちこちで使える関数は便利ですね。

次の関数を見てみましょう。

void showNumber(void)
{
	printf("10\n");
}

この関数は「10」と表示するだけの関数です。
使う機会があるとすれば、10と表示したい時だけですね。
用途は非常に限られてしまいます。

では次の関数はどうでしょうか。

void showNumber( int num )
{
	printf("%d\n" , num);
}

こちらの関数も実用的とは言えませんが、先ほどの関数と違い引数を受け取っています。
この関数は、決まった数値ではなく、呼び出し元から渡された任意の整数値を表示することができます。
最初の関数に比べると、ほんの僅かですが使い道が増えた気がしますね。

ここで引数の役割が見えてきました。

関数に対し、呼び出し元から値を渡すことで関数内の処理を様々に変化させることができます。
引数によって、関数の使い道=「汎用性」が高まったと考えられそうです。

今度は次の関数を見てみましょう。

void sum( int num1 , int num2 )
{
	int ans;
	ans = num1 + num2;
	printf("合計 = %d\n" , ans);
}

呼び出し元から二つの整数を受けとり、その合計値を表示する関数です。
引数を使用しているので、汎用性も問題ないように思います。

一見便利に見えるこの関数ですが、問題点があります。
合計を表示している部分に注目しましょう。

この関数は呼び出すたびに「合計 = 〇〇」と表示されてしまいます。
日本語が読めない方にとっては無意味な表示ですし、合計値を別の計算に使うこともできません。

そう考えると、合計を表示している部分はお節介と言えるのではないでしょうか?
良かれと思って作った機能かもしれませんが、逆に関数の使い道を狭め、便利さを損ねてしまっています。

関数の汎用性を高める、もう一つのポイントは「単機能であること」です。
余計な処理をせず、求められたことだけを行うべきなのです。

ここで登場するのが「戻り値」です。
関数から任意の値を呼び出し元に返すことができます。

int sum( int num1 , int num2 )
{
	int ans;
	ans = num1 + num2;
	return ans;
}

// 呼び出し例
int result;
result = sum( 10 , 20 );
printf("合計 = %d\n" , result);

関数sum内では一切の表示を行わず、合計値だけを求め、戻り値として呼び出し元に返しています。

呼び出し元は、受け取った戻り値をどう使っても構いません。
英語で表示しても、別の計算に使うのも自由です。

つまり、引数も戻り値も「関数の汎用性を高める」ために活躍するものなのです。

引数や戻り値をどうやって決めればよいか迷っている方は、その引数/戻り値によって、関数の汎用性が向上するか?を判断材料にしてみると良いかもしれません。

関数はとても奥が深く、考えれば考えるほど新たな迷いが出てきます。
私たちが普段、何気なく定義している関数は、本当に「便利」なのでしょうか?
written by Tomoji Onishi / 2023.03.15
コラム一覧