再帰を学ぼうとしています。このコードを自分の本からコピーし、メソッドが何をいつ実行しているかを追跡できるように表示を追加しました。
public static void main(String[] args) {
System.out.println(sum(4));
}//main
public static int sum(int n) {
int sum;
System.out.println("n = " + n + "\n");
if (n == 1)
sum = 1;
else
sum = sum(n - 1) + n;
System.out.println("n after function = " + n + "\n");
System.out.println("sum after function = " + sum + "\n");
return sum;
}
結果は次のとおりです。
n = 4
n = 3
n = 2
n = 1
関数後のn = 1
関数後の合計= 1
関数の後のn = 2
関数後の合計= 3
関数の後のn = 3
関数後の合計= 6
関数の後のn = 4
関数後の合計= 10
10
スタックに何が保存されているのか、おそらくそれがどのように保存されているのかがわからなくなってきています。
Nが1までカウントダウンしている間にスタックの内容を表示する方法はありますか?
これは奇妙な概念です。これは、理解できない方法で値を格納する目に見えないループのようなものです。
Javaコードからスタックの内容に直接アクセスすることはできませんが、デバッガ(すべての最新のIDEに統合されています)を使用すると、さらに優れたものが得られます。メソッド呼び出しのスタックがリストされます。そして、各レベルで、それを選択すると、スタック上のローカル変数の値。
これが Eclipseでデバッガーを使用する方法に関するチュートリアル です。
Eclipseなどのデバッガーを使用して、いつでもスタックを表示できますが、ループを再帰として想定することは、ループを理解するための最良の方法ではありません。スタックを下っていくと、スタックの一番下に到達するまで問題の小さな部分を分解します。問題が解決するのは簡単です。次に、スタックを元に戻すときに、ピースを再び元に戻します。
_sum(4)
sum(3) + 4
sum(2) + 3
sum(1) + 2
1
_
sum(4)
はsum(3) + 4
に分解されます。 sum(3)
はsum(2) + 3
に分解されます。やがてsum(1)
に到達します。これには、_1
_という簡単な答えがあります(if
ステートメントの最初の選択肢)。
さらに分解する方法はないので、スタックに戻って_2
_、_3
_、および_4
_を追加します。同じ計算を行うことができますが、それはループとは非常に異なるもので、スタックを上下に移動します。
スタックにはアクセスできますが、スタックの値にはアクセスできません。
public class StackTrace {
public static void main(String[] args) {
recurse(10);
}
static void recurse(int depth) {
for (StackTraceElement each: new Exception().getStackTrace()) System.out.println(each);
if (depth > 0) recurse(depth - 1);
}
}
もちろん、独自のCスタイルのスタックを維持するプログラムを作成することもできます。この場合、すべての引数とローカルをプッシュし、スタックに値を返します。
public class Main {
public static void main(String[] args) {
new Main().run();
}
int[] stack = new int[100];
int base = 0;
public void run() {
// Push arguments on stack
stack[base] = 4;
// increase base pointer and call method
base += 1;
call();
// retrieve result
int result = stack[base];
// print it
System.out.println(result);
}
public void call() {
// Push locals on stack and increase base, now arg n
// is at base - 2 and local sum is at base - 1
stack[base] = 0;
base += 1;
// test if n equals 1
if (stack[base - 2] == 1) {
// if yes set sum to 1
stack[base - 1] = 1;
} else {
// Push new argument on stack
stack[base] = stack[base - 2] - 1;
// increase base pointer and call method
base += 1;
call();
// add result to sum
stack[base - 1] += stack[base];
// add n to sum
stack[base - 1] += stack[base - 2];
}
// cache return value
int result = stack[base - 1];
// decrease base pointer by size of both args and locals
base -= 2;
// Push result on stack
stack[base] = result;
}
}
プログラムでは、呼び出し元が引数のプッシュとベースポインターの増加を担当し、レシーバーがベースポインターをリセットしてスタックに戻り値を残すという呼び出し規約を使用しています。
これは、メソッド呼び出しがマシンレベルで行われる方法です。