哪些命令式编程语言不支持递归?


21

据我所知,所有现代命令式编程语言都支持过程可以调用自身的递归。并非总是如此,但是我无法通过Google的快速搜索找到任何困难的事实。所以我的问题是:

哪些语言从一开始就不支持递归?何时添加了这种支持?

Answers:


21

我不确定COBOL是否这样做(当然一次也没有),但是我也无法想象有人在乎什么。

Fortran自Fortran 90起就有,但要求您使用recursive关键字告诉它子例程是递归的。

PL / I几乎相同-支持递归,但是您必须明确告诉它哪些过程是递归的。

我怀疑还有更多。当您开始研究时,禁止递归是IBM在其语言设计中所做的主要工作,原因很简单,因为IBM(360/370/3090 / ...)大型机不支持硬件堆栈。当大多数语言来自IBM时,它们大多禁止递归。现在它们都来自其他地方,总是可以使用递归(尽管我应该补充一点,其他一些机器,特别是原始的Cray 1,也没有对堆栈的硬件支持)。


该时期的Control Data计算机也不支持递归(子例程调用是通过一条指令进行的,该指令修改了代码以插入到调用指令+ 1的跳转)。当Wirth在6600上开发Pascal时,他大概不得不想出一种新的方式来调用子例程。
David Thornley,2010年

@David:是的-并非偶然,它们也是由Seymour Cray设计的。我曾经看过Pascal 6000编译器,但是不记得看过它生成(模拟?)堆栈帧的情况。
杰里·科芬

notably the original cray 1因此,您不需要递归来克隆恐龙吗?我想这真的取决于我们猴子在树上摇摆。
normanthesquid 2011年

2
甚至CAML(和OCAML,F#)也需要显式标记的递归函数。
jk。

1
@Panzercrisis:我不确定IBM是否参与了x86,但是它们当前的大型机可以直接追溯到1964年上市的IBM 360,因此基本设计比x86早了几十年。
杰里·科芬2014年

16

维基百科说:

像Fortran这样的早期语言最初并不支持递归,因为变量是静态分配的,并且返回地址的位置也是如此。

http://en.wikipedia.org/wiki/Subroutine#Local_variables.2C_recursion_and_re-entrancy

FORTRAN 77不允许递归,而Fortran 90不允许(必须明确声明递归例程)。

大多数FORTRAN 77编译器都允许递归,有些(例如DEC)要求使用编译器选项(请参见编译器选项一章)。严格遵守Fortran 77标准的GNU g77根本不允许递归。

http://www.ibiblio.org/pub/languages/fortran/ch1-12.html


iirc至少有一个FORTRAN 77编译器,尽管它在技术上支持递归,但您可以拥有的递归堆栈堆栈总数如此之小,无法有效地用于许多问题
jk。

6

OpenCL编程语言不支持递归。(请参阅OpenCL规范的 6.8节)

这样做的当前动机是:a)缺乏用于深层堆栈的空间b)希望静态地知道总所需分配,以便在存在大量寄存器集和大量内联的情况下优化性能。

这很可能适用于其他GPU编程语言,例如着色器语言。


2

一些用于小型微控制器的c编译器不支持递归,大概是因为它们的堆栈大小非常有限。


其中一些微控制器(例如PIC16系列)仅具有硬件调用堆栈(不能通过指令访问),并且没有任何其他形式的堆栈,因此使用递归时函数不能具有局部变量(因为显然需要数据堆栈)为此...)参考:en.wikipedia.org/wiki/PIC_microcontroller#Stacks
Ale

1

在行号时代,BASIC的递归支持往往较差。当时的许多(全部?)BASIC支持嵌套的gosub调用,但是不支持通过传递参数或返回值的简便方法来使其对自调用有用。

许多早期的计算机都存在递归问题,因为它们使用调用指令将返回地址写入被称为例程(PDP8,IAS机器家族,可能是我不熟悉的更多体系结构)的开头,通常以这种方式是“跳转到调用例程的指令后跳转到指令”的机器代码。


1

这取决于您所说的“ 支持 ”。为了支持递归,您需要一个堆栈,该堆栈在每次重新进入时都会重新实例化局部变量。

即使该语言没有局部变量的概念,如果它具有“子例程”的概念并且可以管理相同变量(也称为数组)之间的索引,则可以在每次进入/退出时增加/减少全局索引函数的功能,并通过它访问一个或多个数组的成员。

我不知道这是否可以称为“支持”。事实是,我使用ZX-Spectrum BASIC编写了递归函数,就像我在Fortran77和COBOL中所做的那样……总是有这个技巧。


1

汇编语言不直接支持递归-您必须“自己动手”,通常是将参数压入机器堆栈中。


2
它支持递归,只要它支持方法调用即可。通常有一条CALL指令,它会在跳转到子例程之前自动将IP推入堆栈,还有一条指令会将RET返回地址弹出到IP中。您没有理由不能CALL拥有自己的入口点。
Blorgbeard'8

@Blorgbeard-绝对正确,尽管我认为这在通常理解的意义上不足以算作“支持递归”,因为它不处理递归调用所需的参数。
mikera

1
好吧,递归调用在技术上不需要参数,对吗?void f() { f(); }是递归的。
Blorgbeard'8

从技术上讲,没有。但是,能够编写一个简单的案例并不意味着恕我直言,这并不意味着您应该将程序集描述为“支持递归”。递归的大多数实际用途都需要参数。
mikera'8

我想你可以这么说。但是在这种情况下,汇编程序也不支持循环(您必须手动进行CMP和JNZ)。我想这与您所谓的“支持”有关。
Blorgbeard 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.