静的変数を使用せずに、Java のヘルパー関数を介して情報を保持することは可能ですか。
例えば、
public void foo(){
int v = 0;
fooHelper(2);
}
public void fooHelper(int depth){
v++;
fooHelper(depth-1)
}
つまり、関数外の変数にアクセスすることなく、再帰ケースごとに情報を失うことなく、変数 v を更新したいと考えています。
属性を宣言したり、各再帰呼び出しで変更可能なオブジェクトを更新したりするように指示するすべての回答を忘れてください。真の機能的で再帰的なスタイルでは、情報をパラメーターや戻り値として渡すことで「保持」します。
簡単な例で説明しましょう。 の要素の合計を再帰的に計算したいとしましょうint[]
。ここで、状態(再帰呼び出し間で保持する必要がある情報) は、配列内の現在のインデックスとそれまでの合計です。方法は次のとおりです。
public int sum(int[] array) {
return sum(array, 0, 0);
}
private int sum(int[] array, int idx, int acc) {
if (idx == array.length)
return acc;
return sum(array, idx+1, acc+array[idx]);
}
次のように呼び出します。
int[] array = {1, 2, 3};
System.out.println(sum(array));
ご覧のとおり、(静的またはインスタンス) 属性を宣言する必要はなく、変更可能なオブジェクト (リスト、マップ) を渡したり変更したりする必要もありません。問題はメソッドのパラメーターとして存在します。
あなたの質問のコードでは、v
変数はacc
私の答えでパラメーターが行っていること、つまり、再帰が呼び出されるたびに累積値を変更することになっています。最後に、ヘルパー関数 (戻り値の型を持たない必要がありますvoid
) から累積された値を返すだけでよく、それが で値を取得する方法ですfoo()
。
スコープ (メソッドなど) で宣言された変数は、このスコープでのみアクセスできます (たとえば、別のメソッドではアクセスできません)。
情報がメソッドのみに関連する場合は、変数をメソッドに保持します。情報がオブジェクト/クラスの状態全体に関連している場合は、それをクラス メンバー (静的/非静的) のままにします。
例えば:
public void someRecursiveMethod(int num) {
while (num < 10) {
num++;
someRecursiveMethod(num);
System.out.println("Current num = " + num);
}
}
変数vはプリミティブ型であるため、変数vに加えられた変更は関数スコープの外には表示されません。クラス内で変数vを宣言し、たとえばStateを宣言し、状態オブジェクトを再帰関数に渡して、必要な効果を得ることができます。
public void foo(){
State state = new State();
fooHelper(state, 2);
}
public void fooHelper(State state, int depth){
state.v++;
fooHelper(state, depth-1);
}
class State {
int v;
}
それが役に立てば幸い。
オブジェクトを渡して、再帰呼び出しごとに更新を保存できます。以下のようなもの。
public static void fooHelper(int depth, HashMap map){
map.put(depth, "Call " + depth);
if (depth > 0)
{
fooHelper(depth-1, map);
}
return;
}
これはメモ化と呼ばれていると思います。のように見えます
class Fibonacci
{
public Map < Integer , Integer > memorized = new HashMap < > ( ) ;
public int fib ( int n )
{
if ( memoized . containsKey ( n ) )
{
return memoized . get ( n ) ;
}
else
{
int fib = // calculate recursively
memoized . put ( n , fib ) ;
return fib ;
}
}
}
このアルゴリズムからまともな(最適ではない)パフォーマンスを得ることができるはずです。再帰的フィボナッチアルゴリズムのパフォーマンスがひどい主な理由は、同じ値を繰り返し計算しているb/cです。recursion + memoizationを使用すると、値を2回以上計算することはありません。
暗記と暗記の微妙な違いを指摘してくれた@Aristideに感謝します。
インスタンス変数(必ずしも静的である必要はありません)にしないのはなぜですか... ??
public class Recursive {
int v = 0;
public void foo(){
fooHelper(2);
System.out.println(v);
}
public void fooHelper(int depth){
v++;
if(depth-1!=0)//Added this because I was getting an StackOverflowError
fooHelper(depth-1);
}
public static void main(String[] args) {
Recursive r = new Recursive();
r.foo();
}
}
リストまたは同様のデータ構造を返すことができます。
public List<Integer> fooHelper( int v, int depth ){
if( depth == 0 ) return new ArrayList();
v++;
List<Integer> result = fooHelper( v, depth-1 );
result.add( new Integer(v) );
return result;
}
新しいクラスを作成するか (yuck)、変数をパラメーターとして渡して fooHelper に返すことができます。