1.1 程序设计语言设计的艺术
如今有数千种高级程序设计语言,且新的语言不断涌现。为什么有这么多?有几种可能的答案:
技术演进。计算机科学是一门年轻的学科;我们一直在寻找更好的解决方案。20世纪60年代末和70年代初发生了一场“结构化编程”的革命,其中基于
goto
控制流的Fortran、Cobol和Basic3等语言被while
循环、case
(switch
)语句和类似的高级结构所取代。20世纪80年代末,嵌套块结构的Algol、Pascal和Ada等语言的开始让位给Smalltalk、C++、Eiffel等面向对象的语言,以及十年后的Java和C#。为了快速开发,最近Python和Ruby等脚本语言开始取代更传统的编译语言。特殊用途。有些语言是为特定的问题域设计的:各种Lisp方言都适合处理符号数据和复杂的数据结构;Icon和Awk很适合处理字符串;C语言适用于底层系统编程;Prolog很适合于推理数据之间的逻辑关系。这些语言中的每一种都可以用于更广泛的任务,但重点显然是专业性。
个人喜好。不同的人喜欢不同的东西,编程的狭隘性很大程度上只是一个品味问题。有些人喜欢C的简洁,但是有些人讨厌它;有些人觉得递归思考很自然,但是有些人更喜欢迭代。有些人喜欢使用指针,但是有些人更喜欢Lisp、Java和ML中的隐式解引用。个人偏好的程度和多样性使得任何人都不可能开发出一种普遍接受的编程语言。
当然,有些语言比其他语言更成功。在已经设计的许多语言中,只有几十种被广泛使用,那么是什么让一门语言成功呢?同样,也有几个不同的答案:
表达能力。人们通常会听到这样的论点,即一种语言比另一种语言更“强大”,尽管在形式上的数学意义上,它们都是图灵完备的,也就是每种语言都可以用来实现任意算法,即使可能很笨拙。尽管如此,语言特性对程序员编写清晰、简洁和可维护代码有着巨大的影响,特别是对于非常大的系统。例如,早期的Basic与C++之间没有可比性。语言表达能力的强弱,特别是抽象表达能力的强弱,是本书的一个主要关注点。
学习曲线。虽然很容易选择Basic,但不能否认它的成功。这种成功的部分原因是它的“学习曲线”很低。长久以来,使用Pascal作为编程语言入门课程是因为至少与其它“严肃”语言相比,它紧凑且易于学习。世纪之交后不久,Java开始扮演类似的角色,虽然实际上比Pascal更复杂,但它比C++更简单。为了重新追求简单性,近年来一些入门课程转向了Python等脚本语言。
实现难易。除了它的低学习曲线外,Basic之所以成功,是因为它可以在资源有限的小型机器上轻松实现。出于类似的原因,Forth有一小部分忠实的追随者。Pascal成功的一个重要因素可以说是其设计者Niklaus Wirth开发了一种简单、可移植的实现,并将其免费提供给世界各地的大学(参见示EXAMPLE 1.15)4。Java和Python设计者采取了类似的方式:语言几乎可以免费提供给任何人。
语言标准。几乎每一种广泛使用的语言都有一个官方的国际标准或者一个规范化的实现(对于几种脚本语言而言),后一种情况下,规范的化实现几乎总是用有国际标准的语言编写的。语言和库的标准化是确保代码跨平台和可移植的唯一有效的方法。Pascal的标准相对贫乏,许多程序员认为其缺少一些关键特性(分离编译、字符串、静态初始化、随机访问I/O),这至少是该语言在20世纪80年代走向没落的部分原因,因为这些功能由不同的厂商以不同的方式实现。
开源。大多数编程语言都至少有一个开源编译器或解释器,但有些语言,尤其是C语言,和其它自由发布、同行评审、社区支持的语言相比,与开源的联系更为紧密。C语言最初是由贝尔实验室5的Dennis Ritchie和Ken Thompson于20世纪70年代初在设计Unix操作系统时开发的。多年来,Unix逐渐发展成为世界上最广泛使用的操作系统——计算机学术界的首选操作系统,C语言也一直与之密切相关。随着C语言的标准化,该语言可以在各种平台上使用,例如世界领先的开源操作系统Linux是用C语言编写的。截至2015年6月,C语言及其子系统相关内容占据了与语言相关内容的一半以上,包括网页引用、图书销售、职位列表和开源存储库更新。
优秀的编译器。Fortran的成功很大程度上归功于编译器。在某种程度上,这是一个历史性的偶然事件。Fortran的存在时间比任何其它语言都长,公司投入了大量的时间和金钱来编写能够生成更快代码的编译器。然而这也是语言设计的问题:Fortran 90之前的方言缺乏递归和指针,因为这些特性会使得生成快速代码变得非常复杂(至少对于没有递归和指针的程序来说是如此!)。类似地,一些语言(如Common Lisp)之所以成功,部分原因是它们的编译器和工具链可以很好的帮助程序员管理大型项目。
经济、赞助和惯性。除了技术方面,还有其它因素对语言的成功有很大影响。强大赞助商的支持就是其中之一。近似来说,PL/I由IBM资助,Cobol和Ada由美国国防部资助。C#由微软资助。近年来,作为iPhone和iPad应用程序的官方语言的Objective-C受到了极大的欢迎。在生命周期的另一端,一些语言在有了“更好”的替代品后仍然被广泛使用了很长时间,因为更换已经安装的软件和程序员的成本太高。例如,世界上许多金融基础设施仍然使用Cobol。
显然没有任何单一因素决定一门语言是好是坏。当我们学习编程语言时,我们需要从多个角度考虑问题。特别是我们需要从程序员和语言实现两方面考虑。有时这两方面是一致的,比如在执行速度上,但是通常会有冲突和权衡,因为功能与其实现成本是平衡的。当实现不仅对使用该功能的程序增加成本,而且对不使用该功能的程序也增加成本时,权衡变得尤为棘手。
在计算机发展的早期,实现者的观点占主导地位:程序设计语言是作为告诉计算机要做什么的一种手段发展起来的。然而对于程序员来说,语言是表达算法的一种手段。正如自然语言限制了阐述和论述一样,程序设计语言也限制了可以和不可以轻松表达的内容,并对程序员的思维方式产生了深刻而微妙的影响。Donald Knuth曾建议,编程应被视为一门告诉另一个人希望计算机做什么的事情[Knu84]6。这一定义可能是最好的折衷,因为它承认概念的清晰性和实施效率都是根本问题。本书试图通过同时考虑概念和实施方面涵盖的每个主题来阐述这种折衷思想。
DESIGN & IMPLEMENTATION 1.1 Introduction
Throughout the book, sidebars like this one will highlight the interplay of language design and language implementation. Among other things, we will consider
Cases (such as those mentioned in this section) in which ease or difficulty of implementation significantly affected the success of a language
Language features that many designers now believe were mistakes, at least in part because of implementation difficulties
Potentially useful features omitted from some languages because of concern that they might be too difficult or slow to implement
Language features introduced at least in part to facilitate efficient or elegant implementations
Cases in which a machine architecture makes reasonable features unreasonably expensive
Various other tradeoffs in which implementation plays a significant role
4. Niklaus Wirth(1934–),瑞士苏黎世ETH信息学名誉教授,长时间维护一系列有影响力的语言,包括Euler、Algol W、Pascal、Modula、Modula-2和Oberon。这些语言引入了枚举、子范围和集合类型的概念,并统一了记录(结构)和变体(联合)的概念。他在1984年获得了计算机的最高荣誉——年度ACM图灵奖。 ↩
5. Ken Thompson(1943–)领导了Unix的开发。他还设计了B程序设计语言,一种BCPL的子语言,也是C语言的父语言。Dennis Ritchie(1941-2011)是C语言发展背后的主要力量。Thompson和Ritchie是一个极具生产力和影响力的团队的核心。1983年,他们同时获得了ACM图灵奖。 ↩
6. Donald E.Knuth(1938–),斯坦福大学名誉教授,算法设计和分析领域最重要的人物之一,也是众所周知的TEX排版系统(本书就是用它制作的)和TEX文本编程的发明者。他的多卷书《计算机程序设计的艺术》在大多数专业计算机科学家之间享有很高的声誉。他在1974年获得ACM图灵奖。 ↩