[英]What are some good ways of implementing tail call elimination?
比编写编译器和VM更简单的方法是注册和蹦出您的解释器。 既然你有一个解释器而不是编译器(我假设),你只需要几个简单的转换来获得对尾调用的适当支持。
你必须先用延续传递方式编写所有内容,这在C / C ++中可能很奇怪。 Dan Friedman的ParentheC教程将指导您将高级递归程序转换为可机器转换为C的表单。
最后,你将实际上实现一个简单的VM,而不是使用常规函数调用来执行eval,applyProc等,你通过设置全局变量传递参数,然后goto
下一个参数(或使用top-级别循环和程序计数器)...
return applyProc(rator, rand)
变
reg_rator = rator
reg_rand = rand
reg_pc = applyProc
return
也就是说,通常以递归方式相互调用的所有函数都被简化为伪程序集,在伪程序集中,它们只是不会重复出现的代码块。 顶级循环控制程序:
for(;;) {
switch(reg_pc) {
case EVAL:
eval();
break;
case APPLY_PROC:
applyProc();
break;
...
}
}
编辑:我使用JavaScript编写了我的业余爱好Scheme解释器的相同过程。 我利用了很多匿名程序,但它可能有助于作为一个具体的参考。 查看FoxScheme的提交历史,从2011-03-13( 30707a0432563ce1632a )开始到2011-03-15( 5dd3b521dac582507086 )。
编辑^ 2:非尾递归仍会占用内存,即使它不在堆栈中。
在不知道你拥有什么的情况下,我会说最简单(也是最有启发性)的方法是从Dybvig的“Three Implementation Models for Scheme”中实现方案编译器和VM。
我在这里用Javascript完成了(Dybvig的PDF副本也在那里): https : //github.com/z5h/zb-lisp
检查src / compiler.js:compileCons,并在src / vm.js中执行“操作码”
如果您对口译员的实施技术感兴趣,那么Christian Queinnec的书“LiSP - Lisp in Small Pieces”是无法解决的。 它通过完整的代码非常彻底地解释了实现Scheme系统的所有方面。 这是一本很棒的书。
http://www.amazon.com/exec/obidos/ASIN/0521562473/qid=945541473/sr=1-2/002-2995245-1849825
但不要忘记查看ReadScheme.org上的论文。
这部分
编译器技术/实现技术和优化http://library.readscheme.org/page8.html
有很多关于尾调用优化的论文。
除此之外,您还可以找到Dybvig论文(经典)的链接,该论文编写得非常好。 它以非常清晰的方式解释和激励一切。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.