Vla*_*hev 7 javascript lisp recursion scheme
我在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
Run Code Online (Sandbox Code Playgroud)
所以我很高兴,像下面这样的尾递归函数不会用完堆栈:
(define +
(lambda (n m)
(cond ((zero? n) m)
(else (+ (sub1 n) (add1 m))))))
(+ 10000 1) ;=> 10001
Run Code Online (Sandbox Code Playgroud)
但是,不是尾递归的函数,用完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
Run Code Online (Sandbox Code Playgroud)
我如何处理非尾递归函数?有什么选择?什么是好资源?我已经阅读了一些关于trampolining,stack externalizing和continuation-passing风格的内容,但我能找到的是如何用这些样式编写代码,而不是如何使用这些技术来编写解释器.
如果您能够在其他地方保存调用帧信息,则始终可以将调用转变为跳转。这就是“堆栈外部化”所指的。
对于您的解释器,您的调用框架数据需要保存非尾部调用的延续(它本身可能保存进一步的引用,例如它需要访问的任何变量)。每个活动的非尾部调用都需要一个调用帧。
当然,这一切都是用堆栈空间换取堆空间。最终,你并没有真正通过这种方式节省任何内存。:-)