使用するプログラミング言語に関係なく、コーディングを開始するにつれて、コードを鮮明で読みやすく、理解しやすくするための概念を学ぶことができます。 Cにもそのような概念がいくつかあります。それらの1つは、この記事でここで説明する「再帰関数」です。
再帰関数は、それ自体を呼び出す関数です。呼び出しは、関数の本体内から直接行うことも、問題の関数によって呼び出される他の関数内から間接的に行うこともできます。
以下は、直接再帰の例です。
int func (int a)
{
//statements
func(a-1);
// statements
return 0;
}
そして、これが間接再帰の例です:
int func (int a)
{
//statements
func_new(a);
// statements
return 0;
}
int func_new(int b)
{
//statements
func(b-1);
//statementsur
return 0;
}
冒頭ですでに述べたように、再帰はコンパクトなコードを実現するのに役立ちます。コンパクトなコードは、記述が簡単であるだけでなく、理解とレビューも簡単です。この利点をより明確にするために例を見てみましょう。
階乗の概念について聞いたことがあると思います。知らない人にとって、階乗は、整数にそれよりも小さいすべての正の整数を掛けたときに得られる結果です。たとえば、5の階乗は5x4x3x2x1で、これは120に相当します。
数値の階乗を見つけるための簡単なコードは次のとおりです。
#include <stdio.h>
int main()
{
int a = 0, i = 0, fact = 1;
printf("Enter a number: ");
scanf("%d", &a);
for(i=1; i<=a; i++)
{
fact = fact * i;
}
printf("Factorial of the number is: %d ", fact);
return 0;
}
このコードは、Cプログラムを使用して数値の階乗を計算する方法を通知するためのものであることに注意してください。プログラムは、生成される結果の精度に影響を与える可能性のあるコーナーケースを処理しません。
したがって、これは、再帰関数を使用せずに数値の階乗を計算できる多くの方法の1つです。次に、再帰を使用して階乗を計算するコードを見てみましょう。
#include <stdio.h>
int factorial (int b)
{
if(!b)
return 1;
return (b * factorial(b-1));
}
int main()
{
int a = 0, fact = 1;
printf("Enter a number: ");
scanf("%d", &a);
fact = factorial(a);
printf("Factorial of the number is: %d ", fact);
return 0;
}
ご覧のとおり、階乗を実際に計算する関数「factorial」は非常にコンパクトです。そして、細心の注意を払えば、それも非常に理解しやすいです。
何が起こっているのかわからない場合は、ユーザーが入力した値、たとえば5は、「main」関数内から最初に呼び出されたときに「factorial」関数に渡されます。 'factorial'関数の内部には、入力値がゼロかどうかを確認するチェックがあります。これは、関数が入力値'5'で最初に呼び出されたときは当てはまりません。
次に、returnステートメントには、5に「factorial(4)」の戻り値を掛ける式が含まれています。これで、「factorial」関数が再び実行され、次の式に到達します:return(4 * factorial(3))。そして、これらの手順が繰り返されます。
したがって、大まかに見ると、これらの関数呼び出しがスタックされる方法は次のとおりです。
- 5 *階乗(4)
- 4 * facttorial(3)
- 3 *階乗(2)
- 2 *階乗(1)
- 1 *階乗(0)
これで、factorial(0)が実行されると、「factorial」関数の「if」条件が真になり、値「1」が返されます。これで、上記の呼び出しが完了する方法がわかります(前のリストの最後のエントリをこのリストの最初のエントリと比較するなど):
- 1 * 1
- 2 *(1 * 1)
- 3 *(2 *(1 * 1))
- 4 *(3 *(2 *(1 * 1)))
- 5 *(4 *(3 *(2 *(1 * 1))))
これは事実上5*4 * 3 * 2 * 1であり、これは120であり、5の階乗です。
つまり、これが再帰関数のしくみです。これまでにリストした再帰関数の利点については疑いの余地はありませんが、いくつかの欠点もあります。
たとえば、上記の例では、「factorial(0)」の呼び出しが完了するまで、以前の「factorial」の呼び出しはすべて、関数の処理が完了するのを待っていました。自動変数またはローカル変数が、再帰呼び出しごとにメモリを占有するという事実は言うまでもありません。
したがって、再帰を使用する場合、ストレージスペースの節約は事実上ありません。さらに、コードの実行が速くなるという保証もありません。再帰関数の本当の利点は、データ構造を扱う場合です。これについては、この進行中のCチュートリアルシリーズの一部として後で説明します。
結論として、日常のコーディングタスクで再帰関数を頻繁に使用することはないかもしれませんが、それは注意しなければならない重要な概念です。ここで説明した例を試して、再帰関数の概念をさらによく理解するために微調整してください。