今回も、前回に続いて再帰関数のお話です。静的内部変数と自動変数の違いと、引数と戻り値の引継についてのお話です。
今回の要点です。
では、いってみましょう。
第1部第67章でやった静的内部変数のことを思い出してみましょう。
静的内部変数は、関数をいつ、どこで、何回呼んでも同じアドレスのメモリを使っている、というものでした。平たく言えば、変数は1つしか作られないということです。
これに対して、自動変数は関数を呼ぶたびにアドレスが変わりました(同じ事もありますが)。平たく言えば、変数は関数に入るたびに作られるということです。
このことを踏まえると、こんなことができるわけです。
プログラム | 実行結果 |
---|---|
// Recurse3.cpp #include <iostream.h> void Func() { static const int INIT = 5; static int i = INIT; cout << i << endl; i--; if(i > 0) Func(); else i = INIT; } int main() { Func(); Func(); return 0; } |
5 4 3 2 1 5 4 3 2 1 |
前回のグローバル変数 i を、静的内部変数にしました。
先ず、i は INIT によって初期化されます。実は、これは初めて関数に入ったときに1回だけ行われるということになっているのですが、実際はプログラムの初めと考えて構いません。ここではコンストラクタがないからです。コンストラクタがあれば、それが呼ばれたか、呼ばれていないかという大きな違いがありますね。
で、後は前回と同じ事をやります。やはり中で Func を呼ぶわけですが、呼ぶ前の i と呼んだ後の i は同じ i です。ということで、前回と同じような結果になるわけです。
最後、関数を終えるときに i を INIT に戻しておきます。もうこれ以降に中で Func を呼ぶことはないので問題ありません。
もし、これを自動変数にしたらどうなるでしょうか?
実行結果 |
---|
5 5 5 5 5 5 : : : (エラーが出るまで続く) |
関数に入る毎に i が作られます。その時さらに i は INIT で初期化されてしまうので、無限ループになってしまいます。
このように、再帰関数の結果には内部変数が静的かどうかが大きく影響します。
そして、関数の内部で使える変数といえば、もう1つありました。そうです。仮引数です。
仮引数も自動変数と同じく、関数を呼ぶたびに新しい変数が作られます。しかし、この利用価値はとても大きいです。次のプログラムを見て下さい。
プログラム | 実行結果 |
---|---|
// Recurse4.cpp #include <iostream.h> void Func(int nCount) { cout << nCount << endl; if(nCount > 1) Func(nCount - 1); } int main() { Func(3); Func(7); return 0; } |
3 2 1 7 6 5 4 3 2 1 |
先ず、nCount を表示します。次に、これが1より大きければ Func を呼ぶわけですが、このとき nCount - 1 を渡しています。つまり、呼ばれるたびに nCount は1つずつ小さくなっていくのです。
nCount 自体は Func が呼ばれるたびに別の変数を指すのですが、nCount を実引数に取ることで値を引き継ぐことが出来るのです。
このことを利用すると、階乗を求める関数というのも作れますね。階乗というのは、数学でn!と表し、1からnまでの整数を掛けた答になります。
上で表示したのは最初に入れた数から1までの数でした。これをうまく使うことができれば、階乗が求められそうです。
プログラム | 実行結果 |
---|---|
// Recurse5.cpp #include <iostream.h> int Factorial(int num) { if(num > 1) return Factorial(num - 1) * num; else return 1; } void InputAndOutput() { int num; cout << "何の階乗を求めますか? > " << flush; cin >> num; cout << num << " の階乗は " << Factorial(num) << "です。" << endl; } int main() { InputAndOutput(); InputAndOutput(); return 0; } |
何の階乗を求めますか? > 3 3 の階乗は 6です。 何の階乗を求めますか? > 10 10 の階乗は 3628800です。 |
今度は戻り値も利用してます。簡単なので、3を入れたときの動作を例にとって考えてみます。
先ず、num(1) は3です。(カッコに入っているのは、何回目の Factorial の num であるかです。)これは1よりも大きいので、Factorial をまた呼び出します。このとき、num(2) は num(1) - 1 なので、2になります。
で、やはりこれも1より大きいので、Factorial をまた呼び出します。このとき、num(3) は num(2) - 1 なので、1になります。
すると、ここで1以下になったので、Factorial を呼ばずに1を返します。
そして、2回目の Factorial の呼び出しに戻りました。ここで、次は Factorial(num(2) - 1) * num(2) を返します。Factorial(num(2) - 1) の結果は1でした。そして num(2) は2でした。なので、この2回目の Factorial は1×2を返します。
そして、いよいよ1回目の Factorial の呼び出しに戻りました。ここで、最後に Factorial(num(1) - 1) * num(1) を返します。Factorial(num(1) - 1) の結果は1×2でした。そして num(1) は3でした。なので、結局この1回目の Factorial は1×2×3、つまり3の階乗を返すことになります。
このようにすれば、再帰関数を使って階乗を求める関数が作れます。引数だけでなく、戻り値も引き継いでいくことができるわけですね。
しかし、これらの関数は相変わらず for 文を使えば簡単に作ることができます。
int Factorial(int num) { int nFactorial = 1; for(int i = num; i > 1; i--) nFactorial *= i; return nFactorial; }
次回こそは、再帰関数を使った方が便利なことをやってみたいと思います。
それでは、今回の要点です。
では、次回まで。
第21章 call itself | 第23章 call itself 3
Last update was done on 2000.8.25
この講座の著作権はロベールが保有しています