Skip to main content

1.1 入门

计算机科学是一个极其宽泛的学科。全球的分布式系统、人工智能、机器人、图形、安全、科学计算,计算机体系结构和许多新兴的二级领域,每年都会由于新技术和新发现而扩展。计算机科学的快速发展广泛影响了人类生活。商业、通信、科学、艺术、休闲和政治都被计算机领域彻底改造。

计算机科学的巨大生产力可能只是因为它构建在一系列优雅且强大的基础概念上。所有计算都以表达信息、指定处理它所需的逻辑、以及设计管理逻辑复杂性的抽象作为开始。对这些基础的掌握需要我们精确理解计算机是如何解释程序以及执行计算过程。

这些基础概念在伯克利长期被教授,使用的是由Harold Abelson、Gerald Jay Sussman和Julie Sussman创作的经典教科书《计算机科学的构造与解释》(SICP)。这个讲义大量借鉴了这本书,原作者慷慨地使它在创作共享许可下用于改编和复用。这些讲义是在非商业创作共享授权版本3下发行的。

1.1.1 在Python中编程

语言并不是你学到的东西,而是你参与的东西。

Arika Okrent

为了定义计算过程,我们需要一种编程语言,最好是一种许多人和大量计算机都能懂的语言。这门课中,我们将会使用Python语言。

Python是一种广泛使用的编程语言,并且在许多职业中都有它的爱好者:Web程序员、游戏工程师、科学家、学者,甚至新编程语言的设计师。当你学习Python时,你就加入到了一个数百万人的开发者社群。开发者社群是一个极其重要的组织:成员可以互相帮助来解决问题,分享他们的项目和经验,以及一起开发软件和工具。投入的成员经常由于他们的贡献而出名,并且收到广泛的尊重。

Python语言自身就是一个大型志愿者社群的产物,并且为其贡献者的多元化而自豪。这种语言在20世纪80年代末由Guido van Rossum设计并首次实现。他的Python3教程的第一章解释了为什么Python在当今众多语言之中如此流行。

Python适用于作为教学语言,因为纵观它的历史,Python的开发者强调了Python代码对人类的解释性,并在Python之禅中美观、简约和可读的原则下进一步加强。Python尤其适用于课堂,因为它宽泛的特性支持大量的不同编程风格,我们将要探索它们。在Python中编程没有单一的方式,但是有一些习俗在开发者社群之间流传,它们可以使现有程序的阅读、理解,以及扩展变得容易。所以,Python的灵活性和易学性的组合可以让学生们探索许多编程范式,之后将他们新学到的知识用于数千个正在开发的项目中。

这些讲义通过使用抽象设计的技巧和严谨的计算模型,来快速介绍Python的特性。此外,这些讲义提供了Python编程的实践简介,包含一些高级语言特性和展示示例。通过这门课,提高Python技术将会变成自然而然的事情。

开始在Python中编程的最佳方法就是直接和解释器交互。这一章会描述如何安装Python3,使用解释器开始交互式会话,以及开始编程。

1.1.2 安装Python3

就像所有伟大的软件一样,Python具有许多版本。这门课会使用Python3最新的稳定版本。许多计算机都已经安装了Python的旧版本,比如Python 2.7,但是它们可能不满足这门课。你应该可以在这门课上使用任何能安装Python3的计算机。(不要担心,Python是免费的。)

你可以在Python下载页面点击以3开头的版本来下载Python3。按照下面的安装指导来完成安装。进一步的指导可以查看 Juilia Oh 创建的在WindowsMac上安装Python3的视频指导。

1.1.3 交互式会话

在Python交互式会话中,你可以在提示符>>>之后键入一些Python代码。Python解释器读取并求出你输入的东西,并执行你的各种命令。

运行Python3应用来开始一个交互式会话,在终端提示符后(Mac/Unix/Linux)键入python3,或者在Windows上打开Python3应用。(译者注:Windows上设置完Python的环境变量之后,就可以在cmd或PowerShell中执行相同操作了。)

如果你看见了Python提示符>>>,你就成功开启了交互式会话。这些讲义使用提示符来展示示例,同时带有一些输入。

>>> 2 + 2
4

交互控制: 每个会话都保留了你的历史输入。为了访问这些历史,需要按下<Control>-P(上一个)和<Control>-N(下一个)。<Control>-D会退出会话,这会清除所有历史。在一些系统上,上下箭头也会循环历史。

1.1.4 第一个例子

想像会把不知名的事物用一种形式呈现出来,诗人的笔再使它们具有如实的形象,空虚的无物也会有了居处和名字。

—威廉·莎士比亚,《仲夏夜之梦》

为了更好介绍Python,我们会从一个使用多个语言特性的例子开始。下一节中,我们会从零开始,一步一步构建整个语言。你可以将这章视为即将到来的特性的预览。

Python拥有常见编程功能的内建支持,例如文本操作、显示图形以及互联网通信。导入语句

>>> from urllib.request import urlopen

为访问互联网上的数据加载功能。特别是,它提供了叫做urlopen的函数,可以访问到统一资源定位器(URL)处的内容,URL是互联网上的某个位置。

语句和表达式: Python代码包含表达式和语句。广泛地说,计算机程序包含的指令为以下两种

  1. 计算某个值
  2. 或执行某个操作

语句通常用于描述操作。当Python解释器执行语句时,它执行相应操作。另一方面,表达式通常描述产生值的运算。当Python求解表达式时,就会计算出它的值。这一章介绍了几种表达式和语句。

赋值语句

>>> shakespeare = urlopen('http://inst.eecs.berkeley.edu/~cs61a/fa11/shakespeare.txt')

将名称shakespeare和后面的表达式的值关联起来。这个表达式在URL上调用urlopen函数,URL包含了莎士比亚的37个剧本的完整文本,在单个文本文件中。

函数: 函数封装了操作数据的逻辑。urlopen是一个函数,Web地址是一块数据,莎士比亚的剧本文本是另一块数据。前者产生后者的过程可能有些复杂,但是我们可以只通过一个表达式来调用它们,因为复杂性都塞进函数里了。函数是这一章的主要话题。

另一个赋值语句

>>> words = set(shakespeare.read().decode().split())

将名称words关联到出现在莎士比亚剧本中的所有去重词汇的集合,总计33,721个。这个命令链调用了readdecodesplit,每个都操作中间衔接的计算实体:从URL读取的数据、解码为文本的数据、以及分割为单词的文本。所有这些单词都放在set中。

对象: 集合是一种对象,它支持取交集和判断属于的操作。对象整合了数据和操作数据的逻辑,并以一种隐藏其复杂性的方式。对象是第二章的主要话题。

表达式

>>> {w for w in words if len(w) >= 5 and w[::-1] in words}
{'madam', 'stink', 'leets', 'rever', 'drawer', 'stops', 'sessa',
'repaid', 'speed', 'redder', 'devil', 'minim', 'spots', 'asses',
'refer', 'lived', 'keels', 'diaper', 'sleek', 'steel', 'leper',
'level', 'deeps', 'repel', 'reward', 'knits'}

是一个复合表达式,求出正序或倒序都是单词的“莎士比亚词汇”集合。神秘的记号w[::-1]遍历单词中的每个字符,然而-1表明倒序遍历(::表示第一个和最后一个单词都使用默认值)。当你在交互式会话中输入表达式时,Python会在随后打印出它的值,就像上面那样。

解释器: 复合表达式的求解需要可预测的过程来精确执行解释器的代码。执行这个过程,并求解复合表达式和语句的程序就叫解释器。解释器的设计与实现是第三章的主要话题。

与其它计算机程序相比,编程语言的解释器通常比较独特。Python在意图上并没有按照莎士比亚或者回文来设计,但是它极大的灵活性让我们用极少的代码处理大量文本。

最后,我们会发现,所有这些核心概念都是紧密相关的:函数是对象,对象是函数,解释器是二者的实例。然而,对这些概念,以及它们在代码组织中的作用的清晰理解,是掌握编程艺术的关键。

1.1.5 错误

Python正在等待你的命令。你应当探索这门语言,即使你可能不知道完整的词汇和结构。但是,要为错误做好准备。虽然计算机极其迅速和灵活,它们也十分古板。在斯坦福的导论课中,计算机的本性描述为

计算机的基本等式是:计算机 = 强大 + 笨拙

计算机非常强大,能够迅速搜索大量数据。计算机每秒可以执行数十亿次操作,其中每个操作都非常简单。

计算机也非常笨拙和脆弱。它们所做的操作十分古板、简单和机械化。计算机缺少任何类似真实洞察力的事情...它并不像电影中的HAL 9000。如果不出意外,你不应被计算机吓到,就像它拥有某种大脑一样。它在背后非常机械化。

程序是一个人使用他的真实洞察力来构建出的一些实用的东西,它由这些简单的小操作所组成。

—Francisco Cai & Nick Parlante, 斯坦福 CS101

在你实验Python解释器的时候,你会马上意识到计算机的古板:即使最小的拼写和格式修改都会导致非预期的输出和错误。

学习解释错误和诊断非预期错误的原因叫做调试(debugging)。它的一些指导原则是:

  1. 逐步测试: 每个写好的程序都由小型的组件模块组成,这些组件可以独立测试。尽快测试你写好的任何东西来及早捕获错误,并且从你的组件中获得自信。
  2. 隔离错误: 语句中的错误通常可以归于特定的组件模块。当尝试诊断问题时,在你能够尝试修正错误之前,一定要将它跟踪到最小的代码片段。
  3. 检查假设: 解释器将你的指令执行为文字-不多也不少。当一些代码不匹配程序员所相信的(或所假设的)行为,它们的输出就会是非预期的。了解你的假设,之后专注于验证你的假设是否成立来调试。
  4. 询问他人: 你并不是一个人!如果你不理解某个错误信息,可以询问朋友、导师或者搜索引擎。如果你隔离了一个错误,但是不知道如何改正,可以让其它人来看一看。在小组问题解决中,会分享一大堆有价值的编程知识。

逐步测试、模块化设计、明确假设和团队作业是贯穿这门课的主题。但愿它们也能够一直伴随你的计算机科学生涯。