Emacs
emacs-lisp

EmacsLispの再帰の上限回数を増やす

More than 3 years have passed since last update.

概要

EmacsLispの再帰がしょぼいという話を何度か耳にしたので、
何度くらい再帰できるのか、上限を増やせないのか確認してみた。

再帰関数の実装

再帰の回数を調べるためのコードを定義した。

一回実行する度にバッファに数字を挿入するので終了後の数字を見て回数を確認する。

(defun recursive (num)
  "数字をバッファに挿入して数字を増やして再帰呼び出しする"
  (insert (format "\n%d" num))
  (recursive (+ 1 num)))
(recursive 0)

再帰回数の実験

実行すると、580回ほどrecursiveが実行された。

再帰上限をあげる設定

このあたりの変数で上限設定をしているそうなので値を大きくしてみる。

(setq max-specpdl-size 10000) ;; デフォルト 1300
(setq max-lisp-eval-depth 10000) ;; デフォルト 600

再帰上限の実験

複数マシンで試したところ20000前後まで再帰できた。
20000異常は、あまり上限を大きくしすぎてもエラーで停止せずにEmacsが落ちてしまうだけであった。

設定した変数の説明

それぞれの変数の説明は以下。

max-specpdl-size

Variable: max-specpdl-size
この変数は、ローカル変数束縛とunwind-protectのクリーンアップの総数の 上限を定義します(see section 非局所脱出)。
これらは、エラーが通知されるまで (データ"Variable binding depth exceeds max-specpdl-size"による)行なわれます。
この制限を超えると、それに対応するエラーが発生します。これは、適切に定義され ていない関数への無限再帰を防ぐためのものです。
デフォルトの値は 600 です。
GNU Emacs Lispリファレンス・マニュアル: 10. 変数

max-lisp-eval-depth

max-lisp-eval-depth
この変数は、 (エラーメッセージ"Lisp nesting exceeds max-lisp-eval-depth"で) エラーを通知までのeval、apply、funcallの呼び出しの 最大の深さを制限する。 この制限、および、これを超えたときのエラーは、 不正に定義された関数によってLispが無限に再帰することを防止する 1つの方法である。
深さ制限は、Lispコードによる明示的な呼び出しに加えて、 Lisp式で書かれた関数の呼び出しや関数呼び出しの引数や関数本体のフォームの 再帰的な評価などの内部的なeval、apply、funcallの 呼び出しも数える。
この変数のデフォルト値は300。 これに100未満の値を設定すると、指定した値に達するとLispは100に設定し直す。 Lispデバッガに入ったとき、 制限に近い場合にはデバッガ自身が実行できることを保証するために値を増やす。
GNU Emacs Lispリファレンスマニュアル: Eval