JavaScript でおもちゃの Lisp インタープリターを作成しています。JS には末尾再帰の削除 (TRE) がないため、JS で while ループを使用して TRE を実装しました (疑似コード):
function eval (exp, env)
while true
if exp is self evaluating
return exp
else if ...
...
else if exp is a function call
procedure = eval(car(exp), env)
arguments = eval_operands(cdr(exp), env)
exp = procedure.body
env = extend_env(procedure.env, env)
continue # tail call
だから私は満足しており、次のような末尾再帰関数はスタックを使い果たしません:
(define +
(lambda (n m)
(cond ((zero? n) m)
(else (+ (sub1 n) (add1 m))))))
(+ 10000 1) ;=> 10001
ただし、末尾再帰ではない関数は JS スタックを使い果たします (JS コードが であまりにも多く再帰するためeval_operands
):
(define +
(lambda (n m)
(cond ((zero? n) m)
(else (add1 (+ (sub1 n) m))))) ; not proper tail-recursive
(+ 10000 1) ;=> JS: Maximum call stack size exceeded
非末尾再帰関数を処理するにはどうすればよいですか? オプションは何ですか?良いリソースとは?トランポリン、スタックの外部化、および継続渡しスタイルについて少し読んだことがありますが、インタープリターを作成するためにこれらの手法を使用する方法ではなく、これらのスタイルでコードを記述する方法しか見つかりませんでした。