如何通过Ocaml中包含的尾调用获得堆栈跟踪?

时间:2011-09-03 17:54:36

标签: functional-programming ocaml stack-trace tail-call-optimization

ocamldebug中的调用堆栈是真正的调用堆栈,因此进行尾调用的函数不会显示在其中。这令人困惑。如何获得包含尾调用的回溯?

2 个答案:

答案 0 :(得分:8)

最简单的方法是更改​​函数,使其不再是尾递归。这是当我想要在异常中止程序时显示良好的回溯时使用的(在这种情况下不需要ocamldebug,在OCAMLRUNPARAM="b"下运行程序就足够了; documentation)。

我的个人技巧是将尾调用改为

let result = <tail call> in result

Ocaml主要是在编写代码时编译代码,在这种情况下它很棒:编译器不会内联这个并且你得到一个漂亮的回溯。当然,一旦发现错误,您可以轻松删除此优化。 (当你只有几个尾调用时,这种方法很好;如果你有很多尾调用,你可以将整个函数体包装成let result = <body> in result,但我觉得它不太方便和清晰。)

如果你需要函数仍然是taill-call(例如,你有一个OS设置的堆栈大小限制,你可能会耗尽),你可以将此函数的调用堆栈重新调整为数据结构,转为

let rec f arg1 arg2 .. argN =
  ...
  f arg1' arg2' .. argN'

let rec f stack arg1 arg2 .. argN =
 let stack' = (arg1,arg2,..,argN)::stack in
 ...
  f stack' arg1' arg2' .. argN'

然后,您可以在ocamldebug中检查stack变量的值,以获取特定于函数的堆栈跟踪。

答案 1 :(得分:0)

要查看真正的尾部调用的位置,我可以重复键入“start”来反向执行并弹出堆栈,直到我到达感兴趣的调用目标,然后返回。费力,并且必须在逐个呼叫的基础上完成,但它有效。