Compiler construction 将函数转换为使用尾部递归——一个形式化研究
Compiler construction 将函数转换为使用尾部递归——一个形式化研究,compiler-construction,functional-programming,tail-recursion,Compiler Construction,Functional Programming,Tail Recursion,有没有人写过一篇正式的论文,描述一种(自动)将函数转换为尾部递归的方法?我正在寻找大学级别的正式处理,包括限制(可以转换的函数类型)、转换程序,以及(如果可能)正确性证明?哈斯克尔的例子将是一个额外的收获
(自动)将函数转换为尾部递归的方法
这有两个部分:
认识到给定的递归函数可以转换为尾部递归形式,并进行转换
以有效的方式实现尾部调用,因此转换是值得的
将递归转换为尾部递归
从语法上识别尾部递归定义似乎比较直截了当。毕竟,“tail recursive”只是意味着调用在语法上出现在表达式的
有没有人写过一篇正式的论文,描述一种(自动)将函数转换为尾部递归的方法?我正在寻找大学级别的正式处理,包括限制(可以转换的函数类型)、转换程序,以及(如果可能)正确性证明?哈斯克尔的例子将是一个额外的收获
(自动)将函数转换为尾部递归的方法
这有两个部分:
- 认识到给定的递归函数可以转换为尾部递归形式,并进行转换
- 以有效的方式实现尾部调用,因此转换是值得的
将递归转换为尾部递归
从语法上识别尾部递归定义似乎比较直截了当。毕竟,“tail recursive”只是意味着调用在语法上出现在表达式的尾部
例如,人们描述的方案:
仅仅编写适当的自我调用作为跳转并不足以
实现全尾递归。相反,我们在语法上划分所有
源语言中的子表达式位置分为两类:tail
(或减少)位置和子问题位置。简单地说
表达式(如果谓词
后续选项),谓词
是
子问题位置,而结果和替代都处于
减少职位。这个句法概念可以很容易地扩展到
任意嵌套的子表达式
将函数转换为尾部调用
您的问题的棘手部分是识别候选递归计算并将其转换为尾部递归计算的优化
一个参考是在GHC中,它使用内联和大量简化规则来折叠递归调用,直到它们的底层尾部递归结构保持不变
尾部呼叫消除
一旦你有了一个尾部递归形式的函数,你就会希望能有效地实现它。如果你能生成一个循环,这是一个好的开始。如果你的目标机器没有,那么“需要一些技巧。引用下面引用的Odersky和Schinz的话
多年来,人们提出了各种技术来消除
一般(不仅仅是递归)尾部调用,主要针对编译器
针对C
…将整个程序放在一个大函数中并进行模拟
在此函数中使用直接跳转或switch语句调用函数
一种流行的技巧是使用蹦床。蹦床是一种户外运动
重复调用内部函数的函数。每次
希望尾部调用另一个函数,它不直接调用它,而是简单地调用它
将其标识(如闭合)返回给蹦床,然后蹦床执行以下操作:
这样就避免了无限制的堆栈增长,但也提高了一些性能
不可避免地输了,主要是因为蹦床发出的所有叫声都是叫声
到静态未知函数
另一个技巧是亨利·贝克的“切尼在麻省理工学院”
用他的技术,程序首先必须转换为连续传递
样式(CPS),因此将所有调用转换为尾部调用,然后可以
在运行时,当堆栈即将溢出时
当前的续集被构建并返回(或longjmped)到蹦床
“等待”在调用堆栈的底部
- ,Michel Schinz Martin Odersky,2001年
- 小亨利·G·贝克。反对者不应反对其论点,第二部分:切尼
关于M.T.A.草案版本,1994年1月
Mercury包含了一些优化功能,可以自动使事情具有递归性。(是一种逻辑编程语言,所以它谈论的是谓词而不是函数,但Mercury程序和Haskell程序有许多相同的想法。与逻辑而不是函数相比,它的一个更大的区别是严格而不是懒惰)
“蓄能器简介“生成带有额外累加器参数的谓词的专用版本,以便在递归调用之前移动关联操作。显然,这种优化不一定会单独产生尾部递归谓词,但通常会产生一种可以通过第二次优化来优化的形式:
“Last call modulo constructors”本质上允许重写仅由构造函数应用程序执行的递归调用,从而首先构造包含“孔”的值,然后递归调用将其输出直接返回到“孔”的内存地址我相信Haskell会因为懒惰而免费得到这个优化
这两种优化都在本文中进行了描述。我在谷歌上搜索了一下,但找不到任何具体的参考资料。我希望有人能提供一两个参考资料。在最普遍的情况下,CPS转换肯定能完成这项工作(随后的一些优化可能会消除大部分产生的问题).关于这个主题发表了大量的论文。我原以为OP也在寻找尾部递归消除,但问题的措辞似乎与OP相反(甚至是相反的泛化)——引用:“将函数转换为尾部递归[强调我的]”OP询问递归函数到尾部递归形式的自动转换,以便从尾部调用消除中获益。他不是在寻找尾部调用消除本身。问题是模糊的。他不清楚他是在寻找尾部调用消除,还是在g中寻找尾部递归优化