Подтвердить что ты не робот

Java сохраняет информацию в рекурсивной функции

Можно ли сохранить информацию через вспомогательную функцию с помощью java без использования статических переменных.

Например,

public void foo(){
    int v = 0;
    fooHelper(2);
}

public void fooHelper(int depth){
    v++;
    fooHelper(depth-1)
}

А именно, я хочу обновить переменную v без потери информации для каждого рекурсивного случая без необходимости доступа к переменной вне функции.

4b9b3361

Ответ 1

Забудьте обо всех ответах, которые говорят вам объявлять атрибуты, или обновлять изменяемые объекты в каждом рекурсивном вызове. В истинном функциональном рекурсивном стиле вы "сохраняете" информацию, передавая ее как параметры и/или возвращаемые типы.

Позвольте мне проиллюстрировать простым примером, скажем, что вы хотите рекурсивно вычислить сумму элементов в 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().

Ответ 2

Переменная, объявленная в области (например, метод), доступна только в этой области (например, не в другом методе).

Если информация важна только для метода, сохраните переменную в методе. Если информация актуальна для всего состояния объекта/класса, сохраните ее как элемент класса (статический/нестатический).

Например:

public void someRecursiveMethod(int num) {
    while (num < 10) {
        num++;
        someRecursiveMethod(num);
        System.out.println("Current num = " + num);
    }
}

Ответ 3

Вы можете создать новый класс (yuck) или передать переменную в качестве параметра и вернуть ее в fooHelper.

Ответ 4

Почему бы не сделать его переменной экземпляра (не обязательно статическим)...??

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();
    }
}

Ответ 5

Вы можете вернуть список или аналогичную структуру данных:

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;
}

Ответ 6

Поскольку переменная 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;
}

Надеюсь, что это поможет.

Ответ 7

Вы можете передать объект для хранения обновления для каждого рекурсивного вызова. Что-то вроде ниже.

public static void fooHelper(int depth, HashMap map){
      map.put(depth, "Call " + depth);
      if (depth > 0)
      {
        fooHelper(depth-1, map);
      }
      return;
    }

Ответ 8

Я думаю, это называется memoization. Он выглядит как

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, он многократно вычисляет одни и те же значения. С рекурсией + memoization он никогда не вычисляет какое-либо значение более одного раза.

Благодаря @Aristide для указания тонкой разницы между запоминанием и записью.

Ответ 9

[Можно ли задать соответствующий уточняющий вопрос?]

Я понимаю, как считать вверх или вниз: recurse (myString, count + 1); recurse (myString, count-1);

Но какой самый простой способ повторить достижение некоторого базового случая, инициировать его достижение и сообщить кадрам нижнего стека, что достигнут "успех" - что они могут выполнить свою дополнительную работу после возвращения, если я возвращаюсь Строка, и не может вернуть логическое значение?

У меня такое чувство, что я могу: передать булев объект, на который каждый кадр ссылается. Передайте пользовательский объект-оболочку.

Я просто хотел бы лучшие практики...

В моем случае я хочу вернуть k-го предка искомого узла. Медленный бегун, k узлов обратно в стек рекурсии, должен услышать: "найдено!", Чтобы он мог вернуть узел.

Спасибо! (Если мне нужно переместить это, я попытаюсь.)