我正在生成一个随机数列表:
let gen n =
let rec pom l n =
match n with
| 0 -> l
| _ ->
let el = Random.int 20000000
in pom (el::l) (n-1)
in
pom [] n
let lo = gen 1000000
我得到的是
Fatal error: exception Stack_overflow
为什么呢?我正在使用尾递归(和累加器)
修改:
你是对的,堆栈溢出了两种类型。
但是如果我的代码有很多行,那么以这种方式调试它会很痛苦。我想在这里使用ocamldebug,就像学习经验一样。我用这种方式运行ocamldebug:
(ocd) r
Loading program... done.
Time: 88089944
Program end.
Uncaught exception: Stack_overflow
(ocd) b
Time: 88089943 - pc: 52 - module Pervasives
214 | hd :: tl -> <|b|>hd :: (tl @ l2)
(ocd) bt
#0 Pc: 52 Pervasives char 7700
#1 Pc: 64 Pervasives char 7715
#2 Pc: 64 Pervasives char 7715
#3 Pc: 64 Pervasives char 7715
#4 Pc: 64 Pervasives char 7715
#5 Pc: 64 Pervasives char 7715
#6 Pc: 64 Pervasives char 7715
// and so it goes on forever
这告诉我为什么我的程序崩溃了。我怎么能用ocamldebug调试它?
(meta:我应该为它发布一个单独的帖子,还是应该留在这里)
答案 0 :(得分:2)
由于错误的功能不同,以下是您将来如何更快地调试此类事件。
您可以开启回溯。您可以在程序开头调用Printexc.record_backtrace true
,也可以将环境变量OCAMLRUNPARAM
设置为b
来运行,如OCAMLRUNPARAM=b ./a.out
中所示。这应该告诉你错误发生在哪里,虽然有时它会跳过你希望调用堆栈的部分内容 - 我相信这是由于内联等优化所致。但它通常很有用。为此,程序必须使用标志-g
。
通过二进制搜索,您仍然可以找到异常的来源,即使在程序中的一堆函数调用中也是如此。首先将一半函数调用包装在处理程序中。如果在那里发生异常,请打开它们,然后再将其中的一半包裹在处理程序中,依此类推,直到您深入查看源代码。它仍然有点劳动密集型,但您可以在O(log n)时间内以这种方式进行调试,并且数据的日志不是那么多:)
答案 1 :(得分:1)
打印Stack_overflow
异常的回溯通常有些无用,因为导致溢出的调用数超过了回溯缓冲区的大小。例如,如果您采用以下程序(backtrace.ml
):
let init n =
let rec loop xs x =
if x >= 0 then loop (x::xs) (x-1) else xs in
loop [] (n-1)
let sum = function
| [] -> 0
| x :: xs -> List.fold_right (+) xs x
let () =
let xs = init 10000000 in
let y = sum xs in
print_int y
并使用
执行它 OCAMLRUNPARAM=b ocamlbuild backtrace.d.byte --
你会得到一个无用的表格回溯:
Fatal error: exception Stack_overflow
Raised by primitive operation at file "list.ml", line 89, characters 16-37
Called from file "list.ml", line 89, characters 16-37
Called from file "list.ml", line 89, characters 16-37
...
我们不能增加内部回溯缓冲区,但我们可以减小堆栈大小,从而限制回溯的大小,因此它可以适应缓冲区。因此,如果我们在有限的堆栈中运行程序,我们将获得更好的回溯:
OCAMLRUNPARAM="b,l=100" ocamlbuild backtrace.d.byte --
Fatal error: exception Stack_overflow
Raised by primitive operation at file "list.ml", line 89, characters 16-37
Called from file "list.ml", line 89, characters 16-37
Called from file "list.ml", line 89, characters 16-37
...
Called from file "backtrace.ml", line 18, characters 10-16
宾果。问题的根源被精确定位到呼叫站点。
注意:l
的{{1}}选项只能由字节码运行时理解。为了对本机代码重复相同的技巧,应该使用操作系统提供的机制来限制堆栈。对于unices,它通常是OCAMLRUNPARAM
shell原语。
答案 2 :(得分:0)
我在Mac Pro上运行了代码,但没有出现堆栈溢出。正如你所说,你的代码看起来非常好。
可能的解释:
您在内存有限的环境中运行。
您对代码的某些部分有一些旧的定义。也许尝试重新运行一个新的OCaml toplevel。
<强>更新强>
我认为@antron有一个很好的观点。如果您正在运行已编译的代码,则很可能无法确切地知道问题所在。添加一些跟踪,你很可能会发现堆栈溢出在其他地方。