这称为尾递归模Cons
。基本上,在递归调用之后直接添加到列表与在递归调用之前直接添加到列表相同(因此将列表构建为纯功能“循环”的“副作用”)。这是尾递归的概括,它不仅适用于列表,还适用于任何具有常量操作的数据构造函数。cons
1974 年,Daniel P. Friedman 和 David S. Wise 在Technical Report TR19: Unwinding Structured Recursions into Iterations中首次将其描述(但未命名)为 LISP 编译技术,并于 1980 年由 David HD Warren 正式命名并引入编写第一个 Prolog 编译器的上下文。
然而,关于 Oz 的有趣之处在于,TRMC 既不是语言特性也不是显式编译器优化,它只是语言执行语义的副作用。具体来说,Oz 是一种声明式并发约束语言,这意味着每个变量都是数据流变量(或“一切都是承诺”,包括每个存储位置)。由于一切都是承诺,我们可以将函数的返回建模为首先将返回值设置为承诺,然后再实现它。
Peter van Roy 是由 Peter Van Roy 和 Seif Haridi合着的《计算机编程的概念、技术和模型》一书的合著者,他也是 Oz 的设计者之一,也是它的实现者之一,他在评论线程中解释了 TRMC 的工作原理关于 Lambda the Ultimate:尾递归映射和声明性代理:
当直接翻译成 Oz 语法时,上面的不良 Scheme 代码示例会变成良好的尾递归 Oz 代码。这给出了:
fun {Map F Xs}
if Xs==nil then nil
else {F Xs.1}|{Map F Xs.2} end
end
这是因为 Oz 具有单赋值变量。为了理解执行,我们将这个例子翻译成 Oz 内核语言(为了清楚起见,我只给出了部分翻译):
proc {Map F Xs Ys}
if Xs==nil then Ys=nil
else local Y Yr in
Ys=Y|Yr
{F Xs.1 Y}
{Map F Xs.2 Yr}
end end
end
也就是说,Map
是尾递归的,因为Yr
最初是未绑定的。这不仅仅是一个聪明的把戏;它是深刻的,因为它允许声明式并发和声明式多代理系统。