译者:飞龙
第一章和第二章描述了编程的两个基本元素:数据和函数之间的紧密联系。我们看到了高阶函数如何将函数当做数据操作。我们也看到了数据可以使用消息传递和对象系统绑定行为。我们已经学到了组织大型程序的技巧,例如函数抽象,数据抽象,类的继承,以及泛用函数。这些核心概念构成了坚实的基础,来构建模块化,可维护和可扩展的程序。
这一章专注于编程的第三个基本元素:程序自身。Python 程序只是文本的集合。只有通过解释过程,我们才可以基于文本执行任何有意义的计算。类似 Python 的编程语言很实用,因为我们可以定义解释器,它是一个执行 Python 求值和执行过程的程序。把它看做编程中最基本的概念并不夸张。解释器只是另一个程序,它确定编程语言中表达式的意义。
接受这一概念,需要改变我们自己作为程序员的印象。我们需要将自己看做语言的设计者,而不只是由他人设计的语言用户。
实际上,我们可以将许多程序看做一些语言的解释器。例如,上一章的约束传播器拥有自己的原语和组合方式。约束语言是十分专用的:它提供了一种声明式的方式来描述数学关系的特定种类,而不是一种用于描述计算的完全通用的语言。虽然我们已经设计了某种语言,这章的材料会极大扩展我们可解释的语言范围。
编程语言在语法结构、特性和应用领域上差别很大。在通用编程语言中,函数定义和函数调用的结构无处不在。另一方法,存在不包含对象系统、高阶函数或类似while
和for
语句的控制结构的强大的编程语言。为了展示语言可以有多么不同,我们会引入Logo作为强大并且具有表现力的编程语言的例子,它包含非常少的高级特性。
这一章中,我们会学习解释器的设计,以及在执行程序时,它们所创建的计算过程。为通用语言设计解释器的想法可能令人畏惧。毕竟,解释器是执行任何可能计算的程序,取决于它们的输入。但是,典型的解释器拥有简洁的通用结构:两个可变的递归函数,第一个求解环境中的表达式,第二个在参数上调用函数。
这些函数都是递归的,因为它们互相定义:调用函数需要求出函数体的表达式,而求出表达式可能涉及到调用一个或多个函数。这一章接下来的两节专注于递归函数和数据结构,它们是理解解释器设计的基础。这一章的结尾专注于两个新的编程语言,以及为其实现解释器的任务。