自作関数から自作関数の呼び出し
draw
関数から自作関数を呼び出したり、自作関数から Processing の組み込み関数を呼び出したりするのと同じように、自作関数から自作関数を呼び出すことができます。
これを利用すると、長くて複雑なプログラムを、短くて簡単なプログラムの組み合わせによって組み立てることができるようになります。
例えば、整数 n
を受け取り n
未満の素数をコンソールに表示する関数 printPrimesBelow
を考えてみましょう。
プログラムは例えば以下のようになります。
void primesBelow(int n) {
for (int i = 0; i < n; ++i) {
int divCount = 0;
for (int j = 1; j <= i; ++j) {
if (i % j == 0) {
divCount += 1;
}
}
if (divCount == 2) {
println(i);
}
}
}
void setup() {
noLoop();
}
void draw() {
primesBelow(50);
}
ここで、もし i
が素数か判別する関数 isPrime
があるとしましょう。
そうすると、printPrimesBelow
は以下のように書くことができるでしょう。
void printPrimesBelow(int n) {
for (int i = 0; i < n; ++i) {
if (isPrime(i)) {
println(i);
}
}
}
最初のプログラムを比較するとシンプルでわかりやすくなりました。
このような利用ができるように isPrime
関数を実装してみましょう。
プログラム全体は最終的に以下のようになります。
boolean isPrime(int n) {
int divCount = 0;
for (int i = 1; i <= n; ++i) {
if (n % i == 0) {
divCount += 1;
}
}
return divCount == 2;
}
void printPrimesBelow(int n) {
for (int i = 0; i < n; ++i) {
if (isPrime(i)) {
println(i);
}
}
}
void setup() {
noLoop();
}
void draw() {
printPrimesBelow(50);
}
自作関数を適切に利用することで、難しい処理を簡単な処理へと分解することができます。
関数の再帰呼び出し
自作関数から自作関数を呼び出せることを学びました。 それと同じようにある自作関数からその自作関数自身を呼び出すことができます。 これを関数の 再帰呼び出し と呼びます。
配列の最小値を計算する関数を考えてみましょう。 配列 の前から 個の部分配列の最小値を と表すことにします。 ここで、配列 の要素数を とすると の最小値は と表すことができます。
の前から 1 個の部分配列の最小値は、の先頭要素です。 また、もし の前から 個の部分配列の最小値 がわかっているとすると、 の値は、 と の小さい方になります。 これを整理すると以下のように書くことができます。
右辺で が現れることに注目しましょう。 これはの定義に自身が使われています。 このように、ある定義に自分自身を使用することを 再帰的定義 と呼びます。 ある処理が再帰的定義できるとき、再帰呼び出しによってプログラムをシンプルに書くことができます。
ここでは、 の値が 1 ずつ小さくなっていき、最終的に の条件に該当します。 例えば のとき、 のように展開されていきます。
実際に、上記の定義に基づいて、配列の最小値の計算を再帰呼び出しで実装してみましょう。
プログラムをわかりやすくするために、関数名は f
ではなく、arrayMin
とします。
int arrayMin(int[] a, int k) {
if (k == 1) {
return a[0];
}
return min(a[k - 1], arrayMin(a, k - 1));
}
int arrayMin(int[] a) {
return arrayMin(a, a.length);
}
void setup() {
noLoop();
}
void draw() {
int[] a = {
94921,
2398,
71531,
9560,
83280,
};
println(arrayMin(a));
}
配列全体の最小値を求める場合に、k
は a
の要素数となります。
配列全体の最小値を求める場合には、関数のオーバーロードを用いることで第 2 引数を省略できるようにしています。
実行結果は以下のようになり、正しく最小値を求められることが確認できます。
2398
このように、再帰的定義を考えることで処理をシンプルに書くことができる場合があります。