Oz中的尾递归优化

时间:2009-10-03 11:09:19

标签: functional-programming recursion oz

chapter about function in the Oz tutorial中,它说:

  

类似于懒惰的函数式语言   Oz允许某些形式的   尾递归优化   没有发现某些严格的功能   语言包括标准ML,   方案,并发功能   语言Erlang。但是,标准   Oz中的函数定义不是   懒惰。

然后继续显示以下Oz中尾递归的函数:

fun {Map Xs F}
   case Xs
   of nil then nil
   [] X|Xr then {F X}|{Map Xr F}
   end 
end 

这样做,它将空列表映射到空列表和非空列表,将函数F应用到其头部,然后将其前置于调用{{1在尾巴上。在其他语言中,这不是尾递归,因为最后一个操作是前置操作,而不是对Map的递归调用。

所以我的问题是:如果“Oz中的标准函数定义不是懒惰的”,那么Oz做什么,像Scheme或Erlang这样的语言不能(或不会?)能够执行尾递归优化这个功能?确切地说,什么时候函数在Oz中是尾递归的?

2 个答案:

答案 0 :(得分:4)

这称为Tail Recursion Modulo Cons。基本上,递归调用之后直接添加到列表中追加直接添加到之前递归调用(从而将列表构建为"副作用"纯函数"循环")。这是尾递归的推广,它不仅适用于cons列表,而且适用于任何具有常量运算的数据构造函数。

由Daniel P. Friedman和David S. Wise在Technical Report TR19: Unwinding Structured Recursions into Iterations于1974年首次描述(但未命名)为LISP编译技术,并于1980年由David HD Warren正式命名和介绍。编写有史以来第一个Prolog编译器的上下文。

然而,关于Oz的有趣之处在于,TRMC既不是语言特性也不是显式编译器优化,它只是语言执行语义的副作用。具体来说,Oz是一种声明式并发约束语言,这意味着每个变量都是一个数据流变量(或者#34;一切都是一个承诺",包括每个存储位置)。由于一切都是承诺,我们可以建模从函数返回 first 将返回值设置为promise,然后稍后实现它。

彼得·范罗伊是本书Concepts, Techniques, and Models of Computer Programming by Peter Van Roy and Seif Haridi的合着者之一,同时也是奥兹的设计师之一,也是其实施者之一,他解释了TRMC如何在Lambda the Ultimate的评论主题中发挥作用:{{3 }}:

  

上面的错误Scheme代码示例在直接转换为Oz语法时变成了良好的尾递归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最初是未绑定的。这不仅仅是一个聪明的伎俩;它是深刻的,因为它允许声明性并发和声明性多代理系统。

答案 1 :(得分:3)

我对懒惰的函数式语言并不太熟悉,但是如果你在你的问题中考虑函数Map,如果允许堆中的临时不完整的值,则很容易转换为尾递归实现(静音为更完整一次调用一次)。

我必须假设他们正在谈论奥兹的这种转变。 Lispers曾经手工完成这项优化 - 所有值都是可变的,在这种情况下会使用一个名为setcdr的函数 - 但你必须知道你在做什么。计算机并不总是有几千兆字节的内存。手工做到这一点是合理的,可以说它不再是。

回到你的问题,其他现代语言不会自动执行,可能是因为在构建它时可以观察到不完整的值,这必须是Oz找到解决方案的原因。与其他解释它的语言相比,Oz还有哪些不同之处?