前言
多数情况下,计算机程序设计课程是学生修读的第一门计算机科学课程。修读这门课程之前,大多数学生都使用过计算机,例如社交网络、电子邮件、游戏、网页浏览、文字处理和许多其它应用,但直到他们编写第一个程序,他们才开始了解程序的工作原理。在获得了一定程度的程序员技能后(大概是修读过数据结构和算法课程后),下一步自然是想知道程序设计语言是如何工作的。本书对此提供了一个解释,它的目标很简单,就是要成为最全面、最准确的语言教材,同时以一种吸引人的风格,能为多数本科生所接受。这个目标反映了我的信念,即如果我们解释到底发生了什么,学生们会理解得更多,也会更喜欢。
在传统的“系统”课程中,数据结构(可能还有计算机组织)之外的内容往往被划分为许多独立的课程,包括编程语言、编译器构造、计算机体系结构、操作系统、网络、并行和分布式计算、数据库管理系统,可能还包括软件工程,面向对象的设计、图形或用户界面系统。这种划分的一个问题是,课程量不断增加,但学士学位课程的学期数却没有增加。更重要的是,计算机科学中许多最有趣的发现都产生于在学科之间的结合上,例如50多年来,计算机体系结构和编译器伴随着几代超级计算机、流水线微处理器、多核芯片和现代GPU的演进共同发展。在过去的十年里,虚拟化技术的发展模糊了硬件、操作系统、编译器和语言运行时之间的分界线,并推动了云计算的爆炸式发展。程序设计语言技术已经嵌入到从动态网页内容到游戏和娱乐,再到安全和金融的一切事物中。
同时学院派和工程派也越来越强调这种互动,特别是在高等教育领域,核心课程的整合趋势越来越明显。许多学校没有让学生在其它科目上存在短板的情况下只深入研究两三门科目,而是修改了编程语言和计算机组织课程以涵盖更广泛的主题,并在不同专业开设了后续选修课。这一趋势与ACM/IEEECS Computer Science Curricula 2013 guidelines[SR13]非常一致,该指南强调需要控制课程规模,培养“系统级视角”和对理论和实践之间相互联系的理解。此外,该指南特别指出:
“计算机科学专业的毕业生需要从多个细节和抽象层面进行思考。这种理解应该超越各个组件的实现细节,包括对计算机系统结构及其构建和分析过程的理解[p.24]。”
对于本书所涉及的主题,作者写道:
“程序设计语言是程序员精确描述概念、制定算法和推理解决方案的媒介。在整个职业生涯中,计算机科学家会分别或同时使用多种不同的语言。软件开发人员必须理解不同语言的编程模型,并在多个互补的程序设计语言中做出明智的设计选择。计算机科学家通常需要学习新的语言和编程结构,并且必须理解程序设计语言的特性是如何定义、组合和实现的。为了有效使用程序设计语言并认识其局限性,还需要掌握程序设计语言翻译和静态程序分析的基础知识,以及运行时组件,例如内存管理[p.155]。”
本书的前三个版本有幸顺应了综合理解的趋势。第四版加强了“系统级理解”,同时保留了程序设计语言设计的核心关注点。
本书是一本关于程序设计语言如何工作的书。本书不只是简单列举许多不同语言的细节,而是侧重于学生可能遇到的所有语言背后的概念,并用各种具体例子来说明这些概念,并解释不同语言为何以不同方式进行设计。类似地,本书没有解释如何构建编译器或解释器(很少有程序员会完整地完成这项任务),而是关注编译器对输入程序做了什么,以及为什么。因此,程序设计语言的设计和实现是一起探讨的,并着重强调它们相互作用的方式。
第四版的变化
相对于前三版,第四版:
- 新增了专门讨论类型系统和复合类型的章节,删除了旧版中关于类型的单一章节
- 更新了对函数式程序设计的介绍,包含了更多的关于OCaml的内容
- 介绍了程序设计语言领域的许多变化
- 对教师反馈的改进和对相关主题重新整合
第1项可能是最明显的变化。在之前的版本中,第7章是最长的一章,但却包含两部分内容。第四版重新整理了这部分内容,这样可以更加明确地关注类型推理的主题,尤其是它在ML家族语言中的作用。第四版还更新和重组了参数多态性相关内容,这些内容以前分散在几个不同的章节中。
第2项表明,函数式编程技术已经渐渐融入主流命令式语言中,以及SML、OCaml和Haskell在教育界和工业界中的重要性也日益突出。这一版中包含OCaml和Scheme两种语言的函数式编程的例子。如前一段所述,第四版中包含专门的章节(7.2.4)来描述ML类型系统。11.4节是OCaml的概述,介绍了等式和顺序、绑定和lambda表达式、类型构造函数、模式匹配、控制流和副作用。本书选择OCaml而不是Haskell作为ML家族语言的例子反映了它在工业中的地位。同时课堂经验表明,至少对许多学生来说,在具备了函数知识的前提下,最初接触函数式思维更加容易。对于那些希望我选择Haskell语言的同事们,我深表歉意!
其它新内容(第3项)则贯穿全文。在适当的情况下,本书会参考最新语言和标准的功能,包括C&C++11、Java 8、C# 5、Scala、Go、Swift、Python 3和HTML 5。3.6.4节总结了之前对lambda表达式的介绍,并展示了lambda表达式是如何被添加到各种命令式语言中的。10.4.4节补充了对象闭包的内容,包括C++11的std::function
和std::bind
。C-5.4.5节介绍的x86-64和ARM体系结构取代了旧版本中的x86-32和MIPS体系结构,关于调用序列(9.2)和链接(15.6)的章节中的例子也是基于这两种架构的。x86调用序列的描述仅限于gcc,对于ARM则使用LLVM。8.5.3节介绍了智能指针。9.3.1节介绍了右值引用。在9.6.2节的图中,JavaFX取代了Swing。附录A中新增了Go、Lua、Rust、Scala和Swift的内容。
最后,第4项包括对全书几乎每一部分的改进,较为重要的更新包括跟踪(follow)和预测(predict)集(2.3.3节)、递归下降的Wirth错误恢复算法(C-2.3.5节)、重载(3.5.2节)、模块(3.3.4节)、鸭子类型(duck typing)(7.3)、记录和变体(8.1节)、注入列表(intrusive lists)(删除了第10章的中的例子)、静态域(static fields)和方法(10.2.2)、混合继承(从配套网站移回到书中,内容涵盖Scala traits和Java 8的默认方法)、多核处理器(对第13章的普遍修改)、phasers(13.3.1节)、内存模型(13.3.3节)、信号量(13.3.5节)、future(13.4.5节)、GIMPLE和RTL(c-15.2.1节)、QEMU(16.2.2节)、DWARF(16.3.2节)和语言谱系(图A.1)。
为了添加新的内容材料,一些旧的内容的被压缩甚至删除了,包括模块(第3章和第10章)、变体记录和with
语句(第8章)以及元循环解释器(第11章),公共语言基础设施(CLI)已经转移到了配套网站。某些从过时的语言中提取的例子已经被当前所使用的语言替代,几乎所有关于Pascal和Modula的叙述都只是介绍其历史而已。Occam和Tcl的大部分内容也已被删除。
总的来说,本书增加了大约40页,包括5个“设计与实现”侧栏,35个以上的示例,以及大约25个新的节末练习和探索题。为了建立一致和全面的索引,作者投入了大量的精力。正如本书的前几版一样,Morgan Kaufmann公司一直致力于以合理的成本出版本书:本书第四版的价格远低于竞争对手,但内容却更全面。
配套网站
为了在添加新内容后尽量减少本书的厚度,并让学生在阅读时专注于基础知识,我们将大约350页的高级主题或相关内容放在了配套网站上。书中对每个在配套网站(CS)上的章节都进行了简要介绍,“深入了解”部分对省略的内容进行了总结。
注意,配套网站中的内容并不代表着相关技术不重要,它只意味着本书涵盖了更多的内容,而不意味着不是本书的一部分,或者不是一个学期的授课内容。由于偏好和教学大纲各不相同,大多数教师可能会布置配套网站中的阅读内容,更可能会避免讲述书中的某些内容。我的目的是尽可能的将多数课程可能涵盖的内容保留书中。
配套网站中还包括在线资源的链接,以及书中所有(二十多种语言)的代码。
设计与实现侧边栏
和前几版一样,这一版依然非常强调语言设计对实现的限制,还有实现对设计影响。140多个“设计与实现”侧边栏强调了其中的联系和相互作用。侧边栏1.1中有更详细的介绍。附录B中列出了编号。
书中带编号例子
本书中的例子与演示流程紧密相连。为了更容易的找到具体的例子、记住它们的内容,并在其它地方引用它们,旁注中标识了每个例子的编号和标题。全书和配套网站中有1000多个这样的例子。详见附录C。
练习题
在每一节的后面(大约每10页)有复习题。复习题都是基于前面的材料,答案都很简单明了。
更详细的问题出现在每章的末尾,这些问题分为练习和探索。前者通常比每节后的复习题更具挑战性,可以作为课后作业或小项目。后者更具开放性,需要查阅网络资料或图书馆、投入大量时间才能得到答案。许多练习(不是探索)答案都可以在这里注册后获取。
如何使用本书
本书几乎涵盖了Computing Curricula 2013 report[SR13]中所有关于PL的内容。本书是为罗切斯特大学的程序设计语言课程设计的,也是报告中(第369-371页)的“范例课程”之一。图1展示了本书可能的几种教学规划。
对于自学或全年课程(图1中的F),我建议从头到尾教授本书,对于每一个“深入理解”部分,参考配套网站上的内容。罗切斯特大学的一学期课程(R)也涵盖了本书的大部分内容,但是不包含配套网站的大部分内容,也不包括自下而上的解析(2.3.4)、逻辑语言(第12章),以及第15章(构建可运行程序)和第16章(运行时程序管理)的后半部分内容。特别注意,有关函数式编程的内容(特别是第11章)可以用OCaml或Scheme语言进行教学。
一些章节(第2、4、5、15、16、17章)比其它章节更强调实现方面的问题,可以调整这些章节的顺序,以作为面向设计的章节的扩展。修读过计算机组织课程学生很可能已经熟悉了第5章中的大部分内容,因此这一章被放在了配套网站上。修读过自动机理论课程的学生也可能已经熟悉了第2章中的一些内容。对于这些章节,可以快速阅读,也可以据此讨论一些实际问题,例如如何从语法错误中恢复,或者扫描器与经典有限自动机的不同。
传统的程序设计语言课程(图1中的P)可能会省略关于扫描和解析,以及第4章的所有内容,还可能从整体上淡化实现方面的问题。当然也可以添加配套网站中面向设计的内容,如多重继承(10.6)、Smalltalk(10.7.1)、lambda演算(11.7)和谓词演算(12.3)。
本书还被一些学校用于编译器入门课程(图1中的C)。典型的教学大纲省略了第3部分(第11章至第14章)的大部分内容,并从整体上淡化了设计方面的内容。除此之外,它还包括所有关于扫描和解析、第15章到第17章,以及配套网站中的部分内容。
本书也可以作为一门半学期的导论课程和两门半学期的可选后续课程(图1中的Q)。导论课程可能涵盖第1、3、6、7和8章的主要(非配套网站)内容,以及第2和第9章的前半部分内容。面向语言的后续课程可能会涵盖第9章的后半部分内容、第3部分的所有内容、配套网站中第6章到第9章的内容,还有关于形式语义学、类型理论或其它相关主题的内容。面向编译器的后续课程可能涵盖第2章的后半部分内容,第4-5章和第15-17章的内容和配套网站中第3章和第9-10章的内容,还有关于自动代码生成、积极的代码改进(aggressive code improvement)、编程工具等内容。
无论以何种方式使用本书,我都假设读者已经掌握了至少一种命令式语言。具体是哪种语言并不重要,因为示例来自多种语言,并配有详细的注释和讨论,没有经验的读者也能够轻松理解。关于这60多种语言的介绍见附录A。算法以伪代码的方式呈现,且是自洽的。代码使用“打字机(typewriter)”字体,伪代码使用为无衬线(sans-serif)字体。
补充材料
除了补充内容外,配套网站还包含所有示例的源代码,以及本书的勘误表。更多资源可在这里获取。对于已使用本书的教师,注册后可以获取的资源包括:
- 书中所有插图的pdf文件
- 教学PPT
- 练习题答案
- 项目的相关建议
致谢
Michael L. Scott
Rochester, NY
August 2015