第一个编译器由Grace Hopper于1952年编写,而Lisp解释器由John McCarthy的学生Steve Russell于1958年编写。编写编译器似乎比解释器难得多。如果是这样,为什么第一个编译器比第一个解释器早六年编写?
第一个编译器由Grace Hopper于1952年编写,而Lisp解释器由John McCarthy的学生Steve Russell于1958年编写。编写编译器似乎比解释器难得多。如果是这样,为什么第一个编译器比第一个解释器早六年编写?
Answers:
编写编译器似乎比解释器难得多。
今天也许是这样,但我要说的是,大约60年前并非如此。原因如下:
基本要点是1950年代的计算硬件环境使得当时考虑到对计算机进行批处理的情况下,只有编译器才可行。
当时,更好的用户界面主要限于打孔卡和电传打印机。1961年,SAGE系统成为第一台计算机上的阴极射线管(CRT)显示器。因此,口译员的交互性直到很晚才成为首选或自然。
1950年代,许多计算机都使用前面板开关来加载指令,而输出只是一排灯/ LED,而到了1970年代,业余爱好者甚至使用前面板开关和LED。也许您对臭名昭著的Altair 8800很熟悉。
其他硬件限制也使解释器不可行。1950年代,计算机中主内存(例如RAM)的可用性极为有限。在半导体集成电路出现之前(直到1958年才出现),存储器仅限于磁芯存储器或延迟线存储器,后者以位或字(无前缀)进行测量。结合二级存储内存(例如磁盘或磁带)的速度慢,即使在加载要解释的程序之前,拥有很多用于解释器的内存,即使不是不可行的,也将被认为是浪费的。
当IBM的John Backus领导的团队在1954-57年创建FORTRAN编译器时,内存限制仍然是一个主要因素。这个创新的编译器之所以成功,仅仅是因为它是一个优化的编译器。
1950年代,大多数计算机几乎没有任何操作系统,更不用说诸如动态链接和虚拟内存管理之类的现代功能,因此,当时解释器的想法过于激进和不切实际。
附录
1950年代的语言是原始的。他们只包括一个小的操作一把,往往是由底层硬件的指示或他们的目标使用的问题定义的影响无论是。
当时,就我们今天所想到的计算机而言,计算机很少是通用计算机。他们无需重新构建就可以重新编程,这被认为是一个革命性的概念-以前人们一直使用机电机器(通常是计算器)来计算或计算答案(1950年代的大多数应用本质上都是数字的)。
从计算机科学的角度来看,编译器和解释器都是翻译器,实现起来的复杂度大致相等。
最初的编程语言非常简单(例如,没有递归),并且接近于本身很简单的机器架构。那时的翻译是一个简单的过程。
编译器比解释器要简单,因为它必须将用于程序执行的数据和用于解释源代码的表都保存在一起。和解释将采取更多的空间,为自己,为程序源代码和符号表。
内存可能太少了(出于成本和体系结构原因),编译器可能是覆盖操作系统的独立程序(我确实使用了其中一种)。为了运行已编译的程序,必须在编译后重新加载OS。...这清楚地表明,为实际工作运行口译员根本不是一种选择。
确实,编译器要求的简单性使得编译器不是很好(完全考虑代码优化仍处于起步阶段)。至少在某些地方,直到60年代后期,手写的机器代码才比编译器生成的代码更有效。甚至有一个代码扩展率的概念,将已编译代码的大小与一个非常好的程序员的工作进行了比较。对于大多数(所有?)编译器来说,它通常大于1,这意味着较慢的程序,更重要的是,较大的程序需要更多的内存。在60年代,这仍然是一个问题。
编译器的兴趣在于简化编程,特别是对于非计算专家的用户,例如各个领域的科学家。这种兴趣不是代码性能。但是,程序员时间仍然被认为是廉价的资源。直到1975年至1980年,成本才花费在计算机上,那时成本从硬件转换为软件。这意味着即使编译器也没有被某些专业人士所重视。
花费大量的计算机时间是使口译员失去资格的另一个原因,以至于这个想法对于大多数人来说都是可笑的。
Lisp的情况非常特殊,因为它是一种极其简单的语言,使它变得可行(并且计算机在58中变得更大了)。更重要的是,Lisp解释器是有关Lisp的自定义性(元循环性)的概念证明,与任何可用性问题无关。
Lisp的成功很大程度上归因于这种事实,即它的自定义性使其成为研究编程结构和设计新语言(以及方便其用于符号计算)的出色测试平台。
我不同意这个问题的前提。
霍珀将军的第一个编译器(A-0)更像是链接器或宏语言。她将子例程存储在磁带上(每个子例程都分配了一个编号),并且她的程序将被编写为子例程和参数的列表。编译器将从磁带中复制所请求的子例程,然后将它们重新排序为完整的程序。
她使用“编译”一词的含义与人们编辑一首诗选集的含义相同:将各种物品收集在一起。
据我所知,第一个编译器没有词法分析器或解析器,这使其成为现代编译器的遥远祖先。她后来创建了另一个编译器(B-0,又名FLOW-MATIC),其目标是使用更加类似于英语的语法,但直到1958年或1959年才与Lisp解释器同时完成。
因此,我认为问题本身是有误的。看来,编译器和解释器几乎同时在同时演化,这无疑是由于共享思想,而这在当时使许多科学家按照相同的思路进行思考。
此处提供引文的更好答案:https : //stackoverflow.com/a/7719098/122763。
等式的另一部分是,编译器是汇编器之上的抽象步骤。首先,我们使用硬编码的机器代码。“我们”是组装者。手工将每个跳转和偏移量等计算为十六进制(或八进制),然后打入纸带或卡片中。因此,当装配工出现在现场时,节省了很多时间。下一步是宏汇编程序。这样就可以编写一个宏,并将其扩展为一组机器指令。因此,Fortran和Cobol向前迈出了一大步。资源(存储,内存和cpu周期)的缺乏意味着通用解释器必须等待技术发展。大多数早期的解释器都是字节码引擎(例如当今的Java或CLR,但功能要少得多)。UCSD Pascal是一种非常流行(且快速)的语言。MS Basic是引擎盖下的字节码引擎。
在指令开销方面,它完全取决于正在运行的处理器。一段时间以来,该行业经历了RISC与CISC的巨大动荡。我亲自为IBM,Data General,Motorola,Intel(当他们出现时),TI和其他一些人编写了汇编程序。指令集,寄存器等之间存在相当大的差异,这将影响“解释” p代码的要求。
作为时间参考,我于1972年左右开始在电话公司编程。
在编写第一个编译器之前,人们编写了汇编代码,与普通的二进制代码相比,这是一个巨大的进步。当时,有一个强有力的论点是,由编译器编译的代码的效率将低于汇编代码-那时(计算机成本)与(程序员成本)的关系与今天有很大不同。因此,从这个角度来看,对编译器有很强的抵抗力。
但是编译器比解释器高效得多。如果您当时建议编写口译员,人们会认为您疯了。您能想象购买一台价值一百万美元的计算机,然后浪费其90%的代码解释能力吗?
在解释循环程序之前,必须将其存储在可以重复读取的介质中。在大多数情况下,唯一合适的介质是RAM。由于代码通常会输入到打孔卡上(对于人类可读的语言而言)很可能是空的,因此在对代码进行存储之前必须对其进行某种处理。在许多情况下,将打孔卡文本处理成适合于处理器直接执行的形式,实际上并不比将其处理成可以通过解释器有效处理的形式更困难。
请注意,早期编译器的目标不是在磁盘上生成汇编语言或目标代码文件,而是在准备好执行的RAM中存储代码。当没有操作系统妨碍时,这实际上非常容易。编译器可以从内存的一端开始生成代码,并从另一端开始分配变量和分支目标。如果一条语句标记有标签“ 1234”,则编译器将在变量“ 1234”中存储一条指令,以跳转到当前代码生成地址,如果该变量不存在,则创建该变量。语句“ goto 1234”(如果不存在)将创建一个变量“ 1234”,然后跳转到该变量(希望在执行该语句之前跳转到存储在其中的正确位置)。goto
一个尚未定义的标签,因为它知道何时goto
编译将跳转到一个变量。这可能不是生成代码的最有效方法,但是对于希望计算机处理的程序大小来说已经足够了。
因为解释器需要编译器才能起作用。
上面的说法并不是真的。严格来说,您可以在不使用编译器或与之不进行交互的情况下进行解释器。但是这样做的结果看起来并不太像我认为这些术语所指的意思。
从严格意义上讲,编译器和解释器做的事情完全不同。编译器从某些来源读取文本,并将其转换为其他格式:汇编语言,机器代码,另一种高级语言,数据结构或其他任何格式。同时,解释器采用某种数据结构,并根据该数据结构执行指令。
如今,我们通常认为的“编译器”实际上是已与代码生成器配对的编译器:该程序从某个来源获取数据,并根据所看到的内容以某种格式输出代码。对于编译器来说,这是相当直观的用法,这是为编译器创建的第一批东西。但是,如果换一种方式来看,这似乎与解释器的工作非常相似。它总是输出代码而不是执行更多常规操作,但是原理是相同的。
如果我们从另一侧看,则解释器需要从某处获取其数据。这只是数据,因此您可以采用与构建其他任何类型的数据相同的方式来构建数据。由于我们正在讨论解释,因此您自然可以根据文本文件中的指令来构建数据。但是要做到这一点,您需要在文本中读取内容并创建数据结构,这就是一个编译器。它被连接到解释器,而不是代码生成器,但是它是一个编译器。
这就是为什么首先编写编译器的原因。即使构想了编译器,解释数据结构的想法也不是什么新鲜事,但是编译器是“缺失的链接”,它使程序员可以用文本构建这些结构。
另一个因素:编写第一个编译器时,机器时间的成本比现在要高得多。口译员会花费更多的机器时间。