Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ssl/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Functional programming 没有调用堆栈的体系结构中的尾部调用_Functional Programming_Callstack_Tail Call Optimization - Fatal编程技术网

Functional programming 没有调用堆栈的体系结构中的尾部调用

Functional programming 没有调用堆栈的体系结构中的尾部调用,functional-programming,callstack,tail-call-optimization,Functional Programming,Callstack,Tail Call Optimization,我对最近的一个问题的回答是用调用堆栈的形式表达的。我担心它不够通用,所以我问你:在没有调用堆栈的体系结构中,尾部调用(或等价物)的概念如何有用 在延续传递中,所有被调用函数都替换调用函数,因此都是尾部调用,所以“尾部调用”似乎不是一个有用的区别。在消息传递和基于事件的体系结构中,似乎没有一个等价物,但如果我错了,请纠正我。后两种体系结构是有趣的例子,因为它们与OOP而不是FP相关。其他架构呢?旧的Lisp机器是基于调用堆栈的吗 编辑:根据“”(和下面的Alex),延续传递下的尾部调用的等价物不是

我对最近的一个问题的回答是用调用堆栈的形式表达的。我担心它不够通用,所以我问你:在没有调用堆栈的体系结构中,尾部调用(或等价物)的概念如何有用

在延续传递中,所有被调用函数都替换调用函数,因此都是尾部调用,所以“尾部调用”似乎不是一个有用的区别。在消息传递和基于事件的体系结构中,似乎没有一个等价物,但如果我错了,请纠正我。后两种体系结构是有趣的例子,因为它们与OOP而不是FP相关。其他架构呢?旧的Lisp机器是基于调用堆栈的吗

编辑:根据“”(和下面的Alex),延续传递下的尾部调用的等价物不是“被调用函数替换调用函数”,而是“调用函数传递给定的延续,而不是创建新的延续”。与我所断言的不同,这种类型的尾部调用是有用的

另外,我对在较低级别使用调用堆栈的系统不感兴趣,因为较高级别不需要调用堆栈。这个限制不适用于Alex的回答,因为他在写其他调用架构()通常有一个等价的调用堆栈,而不是在引擎盖下的某个地方有一个调用堆栈。在连续传递的情况下,结构类似于,但边缘方向相反。调用堆栈等价物与我的兴趣高度相关。

“没有调用堆栈的体系结构”通常在某种程度上“模拟”一个调用堆栈——例如,在IBM 360时代,我们使用了使用寄存器保存区域和参数列表,按照惯例,由某些通用寄存器指示

因此,“尾部调用”仍然很重要:调用函数是否需要保留在调用点之后恢复执行所需的信息(一旦被调用函数完成),或者它是否知道在调用点之后将不会执行,因此只需重用调用方的“信息以恢复执行”即可


因此,例如,尾部调用优化可能意味着不在用于此目的的任何链表上追加恢复执行所需的延续。。。我喜欢将其视为一种“调用堆栈模拟”(在某种程度上,虽然这显然是一种更灵活的安排——不想让连续通过的粉丝们对我的答案一头雾水;-)。

如果这个问题对我以外的人感兴趣,我会对另一个问题有一个答案。这是一个简单的、非严格的版本

当计算系统执行子计算时(即,由于第一次计算取决于第二次计算的结果,因此在执行另一次计算时,计算开始并必须暂停),执行点之间的依赖关系自然产生。在基于调用堆栈的体系结构中,这种关系在拓扑上是一种关系。在CPS中,它是一棵树,其中根和节点之间的每条路径都是一个延续。在消息传递和线程中,它是路径图的集合。同步事件处理基本上是消息传递。启动子计算涉及扩展依赖关系,除非在尾部调用中替换叶而不是附加到叶

< >将尾调用转换为异步事件处理更为复杂,因此应考虑更一般的版本。如果A订阅了通道1上的事件,B订阅了通道2上的同一事件,B的处理程序只在通道1上触发事件(它跨通道转换事件),那么A可以订阅通道2上的事件,而不是订阅B。这是更一般的,因为尾部调用的等效要求

  • 当A在频道2上订阅时,A在频道1上的订阅将被取消
  • 处理程序自行取消订阅(调用时,它们会取消订阅)

现在,对于两个不执行子计算的系统:lambda演算(或一般的术语重写系统)和RPN。对于lambda演算,尾部调用大致对应于一系列缩减,其中项长度为O(1)(参见中的迭代过程)。以RPN为例,使用数据堆栈和操作堆栈(与操作流相反;操作是尚未处理的操作),以及将符号映射到操作序列的环境。尾部调用可能对应于具有O(1)堆栈增长的进程。

是的,在阅读了BTW之后,我意识到我在CPS中关于尾部调用的看法是错误的:一个不错的、真实的、相关的无调用堆栈体系结构的示例是Parrot虚拟机,其控制流完全基于连续性。(有趣的是,它也是基于寄存器的,即它不仅没有调用堆栈,而且也没有数据堆栈。)是的,上周四,Alison Randall在旧金山湾区Python兴趣小组Baypiggies谈到了Parrot(以及她的Python 3编译器Pynie)。我特别问了她关于尾部调用优化的问题,她向我保证这是存在的(有一个小小的转折:一些信息被积累起来,在异常情况下仍然允许提供信息的“非堆栈跟踪”,而一个简单的尾部调用优化不会给出)。