ファイルから大量の SEXP をメモリに読み込もうとしていますが、小さな入力では問題なく動作しているように見えますが、より深くネストされた入力では sbcl がスタックを使い果たします。sbcl が単純に超えられないハードな再帰制限 (1000 関数の深さ) があるようです (奇妙なことに、そのスタック サイズが大きくなった場合でも)。例 (コードはこちら):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)
は再帰を使用しており、そこでスタック オーバーフローが発生しています。もう1つのオプション(私が取り組み始めたもの)は、read
読み取りを再実装する複雑さを避けるために、別のプログラムからフィードしているより制限された構文に依存する反復バージョンを作成することです上記のリポジトリのlisp
ブランチにあります)。
ただし、より標準的なソリューションを希望します。read
再帰を回避することにより、深くネストされた構造を解析できるビルトインの代替手段はありますか?
編集:これは、入力データではなく、sbcl 自体の克服できない問題のようです。簡単な例として、次を実行してみてください。
(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
、どうやら制御スタック サイズは実際には新しいスレッドにのみ適用され、メイン スレッドのスタック サイズは他の多くの要因にも影響されるようです。だから私は別のスレッドに読んでみました。それでもうまくいきませんでした。このレポをチェックアウトしてmake check
、興味があれば実行してください。