【问题标题】:How to keep a "things done" count in a recursive algorithm in Java?如何在 Java 的递归算法中保持“完成的事情”计数?
【发布时间】:2010-09-07 06:33:40
【问题描述】:

我有一个递归算法,它逐个字符地遍历字符串,并对其进行解析以创建树状结构。我希望能够跟踪解析器当前所在的字符索引(对于错误消息以及其他任何内容),但我并不热衷于实现像元组这样的东西来处理多个返回的类型。

我尝试使用 Integer 类型,在方法外部声明并传递给递归方法,但因为它是最终的,所以当我返回时,递归调用增量被“遗忘”。 (因为Integer值的递增使得传值对象引用指向一个新对象)

有没有办法获得类似于工作而不会污染我的代码的东西?

【问题讨论】:

  • 诚然不是一个答案,而是一些同情::-) 几个月前我遇到了类似的情况。由于我使用的是 Common Lisp,我可以简单地将我的计数器声明为“特殊”(即,动态范围而不是词法范围),但我记得当时想“在几乎任何其他语言中,这将需要超过 1 行代码解决”。

标签: java recursion coding-style integer final


【解决方案1】:

这是一种 hack,但有时我会使用可变的 AtomicInteger 来执行此类操作。我也见过传入大小为 1 的 int[] 的情况。

【讨论】:

    【解决方案2】:

    我目前使用的解决方案是:

    int[] counter = {0};
    

    然后将其传递给递归算法:

    public List<Thing> doIt (String aString, int[] counter) { ... }
    

    当我想增加它时:

    counter[0]++;
    

    不是很优雅,但它确实有效......

    【讨论】:

      【解决方案3】:

      整数是不可变的,这意味着当您将它作为参数传递时,它会创建一个副本而不是对同一项目的引用。 (explanation)。

      要获得您正在寻找的行为,您可以编写自己的类,它就像 Integer only mutable。然后,只需将它传递给递归函数,它在递归中递增,当你在递归结束后再次访问它时,它仍然会保持它的新值。

      编辑:请注意,使用 int[] 数组是此方法的一种变体...在 Java 中,数组也是通过引用传递的,而不是像原语或不可变类那样复制。

      【讨论】:

        【解决方案4】:

        既然您已经发现了伪可变整数“hack”,那么这个选项怎么样:

        创建一个单独的 Parser 类对您有意义吗?如果这样做,您可以将当前状态存储在成员变量中。您可能需要考虑如何处理任何线程安全问题,这对于这个特定的应用程序来说可能有点过头了,但它可能对您有用。

        【讨论】:

          【解决方案5】:

          老实说,我会重新编码该函数,使其成为使用循环的线性算法。这样,如果您单步执行一个非常大的字符串,您就没有机会耗尽堆空间。此外,您不需要额外的参数来跟踪计数。

          这也可能使算法更快,因为它不需要对每个字符进行函数调用。

          当然,除非有特定原因需要递归。

          【讨论】:

            【解决方案6】:

            我能想到的一种可能性是将计数存储在类的成员变量中。这当然假设公共的doIt 方法只被单个线程调用。

            另一种选择是将公共方法重构为调用私有辅助方法。私有方法将列表作为参数并返回计数。例如:

            public List<Thing> doIt(String aString) {
                List<Thing> list = new ArrayList<Thing>();
                int count = doItHelper(aString, list, 0);
                // ...
                return list;
            }
            
            private int doItHelper(String aString, List<Thing> list, int count) {
                // ...
                // do something that updates count
                count = doItHelper(aString, list, count);
                // ...
                return count;
            }
            

            这假设您可以在公共doIt 方法中进行错误处理,因为count 变量实际上并没有传递回调用者。如果需要这样做,当然可以抛出异常:

            public List<Thing> doIt(String aString) throws SomeCustomException {
                List<Thing> list = new ArrayList<Thing>();
                int count = doItHelper(aString, list, 0);
                // ...
                if (someErrorOccurred) {
                    throw new SomeCustomException("Error occurred at chracter index " + count, count);
                }
                return list;
            }
            

            如果不进一步了解您的算法的实际工作原理,很难知道这是否会有所帮助。

            【讨论】:

              【解决方案7】:

              您可以只使用一个静态 int 类变量,每次调用 doIt 方法时该变量都会递增。

              【讨论】:

                【解决方案8】:

                你也可以这样做:

                private int recurse (int i) {
                
                    if (someConditionkeepOnGoing) {
                        i = recurse(i+1);
                    }
                
                    return i;
                }
                

                【讨论】:

                • 如果一个函数中有多个递归调用并且前一个计数为 1,则两个新函数调用都将收到参数 2,而其中一个应该得到 3,因为它是第三件事完成。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-03-26
                • 1970-01-01
                • 2017-06-24
                • 1970-01-01
                相关资源
                最近更新 更多