今回も、前回に続いて再帰関数のお話です。静的内部変数と自動変数の違いと、引数と戻り値の引継についてのお話です。
今回の要点です。
では、いってみましょう。
第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
この講座の著作権はロベールが保有しています