1.1 开始
计算机科学是一个极其广阔的学术领域。全球分布式系统、人工智能、机器人、图形学、安全、科学计算、计算机架构以及数十个新兴子领域每年都在不断涌现新技术和新发现。计算机科学的飞速发展已渗透到人类生活的方方面面。商业、通信、科学、艺术、休闲和政治都已经被重新塑造为计算领域。
计算机科学之所以能取得如此高的生产力,是因为它建立在一套优雅而强大的基本思想之上。所有的计算都始于信息的表示、处理信息的逻辑规范,以及设计抽象来管理这种逻辑的复杂性。掌握这些基本原理需要我们精确地理解计算机是如何解释程序并执行计算过程的。
长期以来,这些基本思想都是通过 Harold Abelson、Gerald Jay Sussman 与 Julie Sussman 合著的经典教材《计算机程序的构造与解释》(Structure and Interpretation of Computer Programs, SICP)进行教授的。这本书大量借鉴了该教材,原作者已慷慨地授权在 Creative Commons 许可下进行改编和重用。
1.1.1 Python 编程
A language isn't something you learn so much as something you join.
为了定义计算过程,我们需要一种被人们广泛使用和各类电脑广泛接受的编程语言,在本书中,我们将主要使用 Python 语言。
Python 是一种应用广泛的编程语言,它吸引了来自许多行业的爱好者:Web 程序员、游戏工程师、科学家、学者,甚至新编程语言的设计者。当你学习 Python 时,你就加入了一个拥有数百万开发者的强大社区。开发者社区是非常重要的机构:成员们互相帮助解决问题、分享项目和经验,并共同开发软件和工具。做出贡献的忠实成员通常会获得声望和广泛的尊重。
Python 语言本身是一个庞大志愿者社区的产物,它以其贡献者的 多元化 为傲。该语言由 Guido van Rossum 在 20 世纪 80 年代末构思并实现,他在 Python 3 教程 的第一章解释了在当今众多语言中,Python 为何如此受欢迎。
Python 作为一种教学语言非常出色,因为在其整个历史中,Python 的开发人员一直在强调 Python 代码的人类可读性,并在 Python 之禅 的美观、简洁和可读性的原则下进一步加强。因为它宽泛的特性能够支持各种不同的编程风格,所以十分适合本书,我们将在之后逐一探讨这些风格。从来没有单一的 Python 编程方法,但遵守开发人员社区共享的一组约定会有助于现有程序的阅读、理解和扩展。Python 巨大的灵活性和易学性可以使学生探索许多编程范式,然后将获得的新知识应用到数以千计的 正在进行的项目 中。
这本书秉承了 SICP 的精神:在介绍 Python 功能的同时,也同步介绍了抽象技术和严谨的计算模型。此外,本书还提供了 Python 编程的实用入门指南,包括一些高级语言特性和说明性示例。随着您学习的深入,您对 Python 的熟练程度将自然而然地提高。
开始 Python 编程的最佳方式是直接与解释器进行交互。本节将介绍如何安装 Python 3、启动交互式会话并开始编程。
1.1.2 安装 Python 3
与所有伟大的软件一样,Python 有很多版本,而本文将使用 Python 3 的最新稳定版本。许多计算机已经安装了旧版本的 Python,如 Python 2.7,它们与本书要求不符,你需要使用任意安装了 Python 3 的计算机(别担心,Python 是免费的)。
你可以从 Python 下载页面点击以 3 开头的版本下载 Python 3,并按照安装程序的说明完成安装。
1.1.3 交互式会话
在与 Python 的交互式会话中,你可以在提示符 >>> 后键入一些 Python 代码,Python 解释器会读取并执行你键入的各种命令。
要启动交互式会话,请在终端 (Mac/Unix/Linux) 中键入 python3 或在 Windows 中打开 Python 3 应用程序。
如果你看到了 Python 提示符 >>>,则已经成功启动交互式会话。我们会使用提示符和一些输入来展示示例。
>>> 2 + 2
4交互控制:每个会话都会保留键入内容的历史记录,可以按下 <Control>-P(上一条)和 <Control>-N(下一条)来浏览该记录。使用 <Control>-D 会退出会话并丢弃这些历史。在某些系统上,上、下箭头也可以用于循环浏览历史记录。
1.1.4 第一个例子
And, as imagination bodies forth
The forms of things to unknown, and the poet's pen
Turns them to shapes, and gives to airy nothing
A local habitation and a name.
— William Shakespeare, A Midsummer-Night's Dream
为了正式介绍 Python,我们先从一个使用几个语言特性的例子开始。在下一节中,我们将从头开始,一点一点地构建这门语言。本节可以看作是未来特性的抢先预览。
Python 为广泛的常见编程活动提供了内置支持,例如处理文本、显示图形以及通过互联网进行通信。下面这行 Python 代码:
>>> from urllib.request import urlopen是一个 import 语句,它会导入一个用于“访问互联网数据”的功能,该功能特别提供了一个名为 urlopen 的函数,可以访问统一资源定位符(URL,也就是互联网上的某个网址)上的内容。
语句和表达式。Python 代码由表达式(expressions)和语句(statements)组成。广义上讲,计算机程序包含以下两种指令:
- 计算某个值
- 执行某个操作
语句通常描述操作,Python 解释器每执行一条语句,计算机就会执行相应的操作。另外,表达式通常用于描述计算,当 Python 求值(evaluates)一个表达式时,它会计算出该表达式的值。本章将介绍几种不同类型的语句和表达式。
这条赋值语句(assignment statement):
>>> shakespeare = urlopen('http://composingprograms.com/shakespeare.txt')将名称 shakespeare 与等号 = 后面的表达式的值关联起来。该表达式将 urlopen 函数应用在了一个包含莎士比亚 37 部戏剧完整文本的 URL 上。
函数。函数封装了操纵数据的逻辑。urlopen 就是一个函数,一个网址是一段数据,莎士比亚的戏剧文本是另一段数据。从前者到后者的转换过程可能会很复杂,但我们可以将这种复杂性隐藏在一个函数中,从而能够使用一个简单的表达式来跳过该过程。函数是本章的主题。
另一个赋值语句:
>>> words = set(shakespeare.read().decode().split())将 words 与莎士比亚戏剧中出现的共 33,721 个单词的集合相连。read、decode 和 split 这一连串的命令,每个都作用于一个中间计算实体:从 URL 中读取数据,然后将数据解码为文本,最后将文本拆分为单词放在一个集合中。
对象。集合(set)就是一种对象(object),它支持诸如计算交集和集合关系(membership)等集合操作。一个对象无缝地将数据和操纵该数据的逻辑捆绑在一起,以一种方式管理着两者带来的复杂性。对象是第二章的主题。
最后,这个表达式:
>>> {w for w in words if len(w) == 6 and w[::-1] in words}
{'redder', 'drawer', 'reward', 'diaper', 'repaid'}是一个复合表达式,它求值得到所有莎士比亚戏剧中反向拼写也是单词的集合。神秘符号 w[::-1] 表示枚举一个单词中的每个字母,其中 -1 代表反向。当你在交互式会话中输入表达式时,Python 会在下一行打印它的值。
解释器。复合表达式的求解需要以一个可预测的方式来精确解释代码的过程。实现这样的过程,用于求解复合表达式的程序就称为解释器。解释器的设计和实现是第三章的主题。
与其他计算机程序相比,编程语言的解释器以其通用性而独具一格。Python 设计之初可没想着要去处理莎士比亚全集,然而,它巨大的灵活性使我们能够仅用几条语句和表达式就处理了大量的文本。
最后,我们会发现所有这些核心概念都是紧密相关的:函数是对象,对象是函数,解释器是二者的实例。但是,清楚地理解每一个概念及其在组织代码中的作用对于掌握编程艺术至关重要。
1.1.5 错误
Python 正在等待你的命令。即使你可能还不了解其完整的词汇和结构,我们仍鼓励你尝试使用该语言。当然也请你为错误做好准备,计算机在极其快速灵活的同时也十分死板。斯坦福大学的入门课程 对计算机的本质做出了如下描述:
The fundamental equation of computers is:
computer = powerful + stupidComputers are very powerful, looking at volumes of data very quickly. Computers can perform billions of operations per second, where each operation is pretty simple.
Computers are also shockingly stupid and fragile. The operations that they can do are extremely rigid, simple, and mechanical. The computer lacks anything like real insight ... it's nothing like the HAL 9000 from the movies. If nothing else, you should not be intimidated by the computer as if it's some sort of brain. It's very mechanical underneath it all.
Programming is about a person using their real insight to build something useful, constructed out of these teeny, simple little operations that the computer can do.
— Francisco Cai and Nick Parlante, Stanford CS101
当你试着使用 Python 解释器时,计算机的死板会立即显现出来:即使是最小的拼写和格式变化,也会导致预料之外的输出和错误。
学着解释错误和找到错误的原因称为调试(debugging),关于调试的一些指导原则是:
- 增量测试(Test incrementally):每个编写良好的程序都由可以单独测试的小的、模块化的组件组成。尽快测试你写的每一段代码,以便尽早发现问题,并对你的组件建立信心。
- 隔离错误(Isolate errors):语句输出中的错误通常可归因于特定的模块化组件。所以在诊断问题时,将错误追溯到你能找到的最小代码片段,然后再尝试纠正它。
- 检查你的假设(Check your assumptions):解释器会完全按照你的指令执行——不多也不少。当某些代码的行为与程序员假设的行为不匹配时,它们的输出就是不符合预期的。明确你的假设,然后将调试的工作集中在验证你的假设是否真的成立上。
- 咨询别人(Consult others):你不是一个人!如果你不理解错误信息,请询问朋友、老师或搜索引擎(译注:AI 解千愁)。如果你已经找出了一个错误,但不知道如何纠正,请别人帮你看一看。在小组解决问题的过程中,会分享很多有价值的编程知识。
增量测试、模块化设计、精确假设和团队合作是贯穿本书的主题,希望它们也将贯穿你的计算机科学职业生涯。