recursion - 了解 Clojure(或 java)中调用堆栈的深度

标签 recursion clojure stack-overflow

Clojure 是我第一次涉足 lisp 世界。我决定尝试一个简单的递归函数,将数字提高到整数次幂。足够简单。

(defmulti pow #(compare %2 0))
(defmethod pow 0 [a n] 1)
(defmethod pow 1 [a n] (* a (pow a (dec n))))
(defmethod pow -1 [a n] (/ 1 (pow a (* -1 n))))

如果你只传递整数幂,它就可以正常工作。但是当我使用它来提高足够大的功率时,我会发生堆栈溢出(可以预见)。

问题是,我如何才能准确地知道函数在死亡之前递归的深度?大致了解递归深度的一种方法是执行以下操作:

(defn max-depth [n] 
    (try
        (max-depth (inc n))
    (catch StackOverflowError err (do
        (printf "%s at %d\n" err n)
        n)
)))

(Clojure 是我第一次接触 lisp,所以我真的不知道如何让代码看起来可读。)这段代码只是无限递归,直到溢出堆栈,然后返回之前发生的递归次数爆炸了。这只能给我一个大概的数字,让我知道我可以走多深……这种方法存在很多问题。

我能做的另一件事是捕获异常并尝试自己展开堆栈...但我真的没有找到一种方法来从异常中获取我想要的信息。查看 StackOverflowError 的 javadoc http://docs.oracle.com/javase/6/docs/api/java/lang/StackOverflowError.html我看到了一些看起来很有希望的方法,但据我所知,没有任何结果。

我尝试运行 (count (.getStackTrace error)) ,其中错误是我捕获的堆栈溢出,但结果仅为 1024,因为“在某些情况下,堆栈帧可能会被忽略”。所以这不起作用。

我能想到的唯一的另一件事是对连续较大的指数运行 pow,但这并不能真正告诉我调用堆栈有多深,它只告诉我可以提高到多大的指数(由于该函数调用了其他函数,因此两个答案并不相同)。

我也不知道用java有什么方法可以做到这一点,但如果有的话,那么这个答案也可能会有所帮助。

有什么想法吗? 谢谢, ——斯科特

最佳答案

我认为 (.getStackTrace error) 是你最好的选择 - 为什么你认为它不起作用?

在一个简单函数的堆栈销毁之前,递归深度为 1024 听起来符合我的预期。

请记住,单个 Clojure 函数通常会转换为多个 JVM 方法调用,因此实际的 JVM 堆栈深度可能比递归有效值 n 深得多。如果您想从堆栈跟踪转换回 n,您可能需要计算对多方法的递归调用出现在堆栈跟踪中的次数。

关于recursion - 了解 Clojure(或 java)中调用堆栈的深度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16880385/

相关文章:

c - 如何在递归算法中将结果添加到数组中?

clojure - Leiningen:如何自定义 .m2 文件夹的位置?

c - 举例说明编程中的栈溢出和堆溢出?

java - 递归公式不断返回堆栈溢出

recursion - 如何在没有运行时多态性的情况下对迭代器执行 N 次 `flat_map`(或类似操作)?

c - 如何在c中递归地将字符串反转为空字符串

clojure - 事务内部的deref可能会触发重试-ref状态历史记录的作用是什么?

scala - 与Clojure的线程宏等效的Scala是什么?

haskell - 为什么这个 Haskell 代码会产生堆栈溢出?

c++ - 为什么编译器报告 "operator<< and operator>> recursive on all control paths will cause stack overflow "?