【问题标题】:java retain information in recursive functionjava在递归函数中保留信息
【发布时间】:2012-04-22 05:48:58
【问题描述】:

是否可以通过 java 的辅助函数保留信息,而不使用静态变量。

例如,

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

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

也就是说,我想更新变量 v 而不会丢失每个递归案例的信息,而不必访问函数外部的变量。

【问题讨论】:

    标签: java recursion


    【解决方案1】:

    忘记所有告诉您声明属性或在每个递归调用中更新可变对象的答案。在真正的函数式递归样式中,您通过将信息作为参数和/或返回类型传递来“保留”信息。

    让我用一个简单的例子来说明,假设你想递归地计算int[] 中元素的总和。这里,state(递归调用之间需要保留的信息)是数组中的当前索引和到目前为止的总和。操作方法如下:

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

      【讨论】:

      • 那么静态变量是使用递归的唯一方法吗?
      • @user1220022 绝对不是,在递归期间使用属性存储状态是(通常)没有必要的。看看我的回答,我只使用参数来跟踪状态,没有使用变量
      【解决方案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】:

                我认为这称为记忆。好像

                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 它重复计算相同的值。使用递归+记忆,它永远不会计算任何值超过一次。

                感谢@Aristide 指出记忆和记忆之间的细微差别。

                【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-01-15
                • 1970-01-01
                • 1970-01-01
                • 2013-11-06
                • 2015-09-10
                • 1970-01-01
                相关资源
                最近更新 更多