次の方法で共有


再帰

再帰は、関数で関数自身を呼び出す、重要なプログラミング手法です。 再帰法を使用した簡単な例として、階乗の計算があります。 0 の階乗は明確に 1 であると定義されます。 n を 0 より大きい整数とすると、n の階乗は 1 ~ n の範囲にある整数すべての積になります。

再帰の使用

次の段落は、階乗を求める関数の定義です。

"指定された数字が 0 より小さい場合は、計算しません。 整数以外は計算しません。 階乗を求める数字が 0 の場合、階乗は 1 です。 階乗を求める数字が 1 以上の場合は、その数字にその数字より 1 だけ小さい値の階乗を掛けます。"

0 より大きい数値の階乗を計算する場合、他の数値の階乗を少なくとも 1 つ計算する必要があります。 関数を現在の数値に対して実行する前に、1 つ小さい数値に対して自身を呼び出す必要があります。 これが、再帰の一例です。

再帰と反復計算 (ループ) には強い関連があります。再帰と反復計算のどちらでも、関数は同じ結果を返すことができます。 通常、どちらの処理方法が適しているかは、実行する計算によって決まります。プログラマは、その計算に対して最も無理のない方法、または最も適した方法を選択するだけです。

再帰は便利ですが、結果を返さず、終了しない再帰関数を簡単に作成してしまう可能性があります。 このような再帰は "無限" ループになります。 たとえば、階乗の計算の定義から最初の規則 (負の値に関する規則) を省略して関数を作成し、この関数を使用して負の値の階乗を求めようとしたとします。 この場合、たとえば -24 の階乗を計算しようとすると、-25 の階乗の計算が必要となります。 -25 の階乗を計算するためには、さらに -26 の階乗の計算が必要となります。 このように処理が行われていくと、再帰呼び出しは終了しません。

再帰で発生する別の問題は、再帰関数が利用可能なリソース (システム メモリ、スタック領域など) をすべて使用してしまう可能性があることです。 再帰関数が自身を (または、元の関数を呼び出す別の関数を) 呼び出すごとに、いくらかのリソースが使用されます。 これらのリソースは再帰関数が終了するときに解放されますが、再帰のレベルが多すぎる関数は、利用可能なすべてのリソースを使用する可能性があります。 利用可能なリソースがすべて使用されると、例外がスローされます。

このように、再帰関数は注意してデザインする必要があります。 再帰が過度に (または無限に) なる可能性がある場合は、自身を呼び出した回数をカウントし、呼び出し回数を制限するように関数をデザインします。 関数が自身を呼び出した回数がしきい値を超えたら、関数を自動的に終了します。 最適となる最大の反復処理回数は、再帰関数によって異なります。

次に、階乗を求める関数の定義を JScript のコードで示します。 型の注釈を指定して、関数が整数だけを受け取るようにします。 無効な数値 (0 より小さい数) が渡された場合は、throw ステートメントがエラーを生成します。 それ以外の場合は、再帰関数を使用して階乗を計算します。 再帰関数は 2 つの引数を受け取ります。1 つは階乗の引数で、もう 1 つは現在の再帰レベルを追跡するためのカウンターです。 カウンターが最大の再帰レベルに達しなかった場合は、元の数の階乗が返されます。

function factorialWork(aNumber : int, recursNumber : int ) : double {
   // recursNumber keeps track of the number of iterations so far.
   if (aNumber == 0) {  // If the number is 0, its factorial is 1.
      return 1.;
   } else {
      if(recursNumber > 100) {
         throw("Too many levels of recursion.");
      } else {  // Otherwise, recurse again.
         return (aNumber * factorialWork(aNumber - 1, recursNumber + 1));
      }
   }
}

function factorial(aNumber : int) : double {
   // Use type annotation to only accept numbers coercible to integers.
   // double is used for the return type to allow very large numbers to be returned.
   if(aNumber < 0) {
      throw("Cannot take the factorial of a negative number.");
   } else {  // Call the recursive function.
      return  factorialWork(aNumber, 0);
   }
}

// Call the factorial function for two values.
print(factorial(5));
print(factorial(80));

このプログラムの出力は次のようになります。

120
7.156945704626378e+118

参照

概念

型の注釈

その他の技術情報

JScript の関数