我正在尝试从文件中读取大量的sexp到内存中,对于较小的输入似乎工作得很好,但是对于更深层次的嵌套,sbcl会出现堆栈耗尽。似乎有一个硬递归限制(在1000个函数深处),sbcl根本无法超越(奇怪的是,即使它的堆栈大小增加)。示例(代码为here):make check-c
有效,但make check-cpp
耗尽了堆栈,如下所示:
INFO: Control stack guard page unprotected
Control stack guard page temporarily disabled: proceed with caution
Unhandled SB-KERNEL::CONTROL-STACK-EXHAUSTED in thread #<SB-THREAD:THREAD
"main thread" RUNNING
{10034E6DE3}>:
Control stack exhausted (no more space for function call frames).
This is probably due to heavily nested or infinitely recursive function
calls, or a tail call that SBCL cannot or has not optimized away.
PROCEED WITH CAUTION.
Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {10034E6DE3}>
0: ((LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX))
1: (SB-IMPL::CALL-WITH-SANE-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {100FC9006B}>)
2: (SB-IMPL::%WITH-STANDARD-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {100FC9003B}>)
...
为什么我使用递归呢?实际上,我不是,但不幸的是内置(read)
使用递归,而这正是堆栈溢出发生的地方。另一个选项(我已经开始研究)是编写read
的迭代版本,它依赖于我从单独的程序中提供的更有限的语法,以避免重新实现的复杂性read(我的(当前已损坏的)尝试位于上述存储库的lisp
分支中。)
但是,我更喜欢更典型的解决方案。是否有内置read
的替代方法可以通过避免递归来解析深层嵌套的结构?
(for i in $(seq 1 2000); do
echo -n "("
done; echo -n "2"; for i in $(seq 1 2000); do
echo -n ")"
done; echo) > file
然后在sbcl:
(with-open-file (file "file" :direction :input) (read file))
同样的失败发生。
编辑:在#sbcl
上询问,显然控制堆栈大小确实仅适用于新线程,并且主线程的堆栈大小也受到许多其他因素的影响。所以我尝试将读取放在一个单独的线程中。仍然没有奏效。如果您有兴趣,可以结帐this repo并运行make check
。
答案 0 :(得分:1)
我不知道你做了什么(因为你没有完全显示出来),但当我按照以下方式启动sbcl
时,你的例子对我来说很好:
sbcl --control-stack-size 100
当然我推荐使用GNU CLISP和Embedded Common Lisp,因为它们也可以为你的例子提供A-OK。
我将为未来的读者添加对此答案的引用:https://stackoverflow.com/a/9002973/816536
我还要提到,在许多CL实现中,可能需要使用适当的优化选项编译代码,以便从尾部调用优化中受益。