首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >程序化非尾递归消除

问程序化非尾递归消除
EN

Stack Overflow用户
提问于 2013-02-01 03:47:24
回答 1查看 463关注 0票数 7

我正在用JavaScript做一个玩具Lisp解释器。JS没有尾递归消除( TRE ),所以我在JS (伪代码)中使用while循环实现了TRE:

代码语言:javascript
复制
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 

所以我很高兴,像下面这样的尾递归函数不会超出堆栈:

代码语言:javascript
复制
(define +
  (lambda (n m)
    (cond ((zero? n) m)
          (else (+ (sub1 n) (add1 m))))))

(+ 10000 1) ;=> 10001

然而,不是尾递归的函数会耗尽JS堆栈(因为JS代码在eval_operands上重复太多):

代码语言:javascript
复制
(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

如何处理非尾递归函数?有哪些选择?什么是好的资源?我读过一些关于trampolining,堆栈外部化和继续传递风格的文章,但我所能找到的是如何用这些风格编写代码,而不是如何使用这些技术来编写解释器。

EN

回答 1

Stack Overflow用户

发布于 2013-02-01 10:52:06

如果您能够将呼叫帧信息保存在其他位置,则始终可以将呼叫转换为跳转。这就是“堆栈外部化”所指的。

对于您的解释器,您的调用帧数据需要保存非尾部调用的延续(其本身可能包含进一步的引用,例如对它需要访问的任何变量的引用)。每个活动的非尾部调用都需要一个调用帧。

当然,所有这些都是用堆栈空间换取堆空间。最后,这样做并不能真正节省任何内存。:-)

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14633919

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档