“Only One Book”计划(计算机理论类,非TCS)
“Only One Book”计划(计算机理论类,非 TCS)
近年来专业书籍,尤其是计算机类的专业书籍成井喷式增长。在过去这类书也一点不少,但大多是出版社出来骗钱的,能看的没几本,大家公认值得看的书也屈指可数。然而现在情况似乎有些不同了,新一批技术书籍无论是质量还是数量都相比过去有了很大进步,甚至经常出现同一领域有十几本面向读者相差不大的书,且风评都很不错。此外,国产技术书的水平也有显著提升。这固然是好事,但也带来了一些“幸福的烦恼”,那就是选书时往往容易无从下手。
在这样的情况下,我决定以身试毒,给出一份“每个领域一本书”的书单,尽可能不使书中出现太多重复内容,浪费大家时间。即使几本书之间有重复的内容,我也尽量指出哪些部分不用看。我试图总结出一条当前合适且快速的学习路径,尽可能使读者吸收最有价值的内容。
*注:CSAPP 是很好的一本书,但我并未将它放在书单中。这主要是由于 CSAPP 大杂烩式的风格导致它既难以归类到导论性质的书中,又难以归类到任何深入讲解某一领域知识的书中。如果归类到导论中,CSAPP 未免太厚了,如果归类到计组计网操作系统类的书中,CSAPP 在某些层面上又似乎不够深入,因此我并未将其放入该书单。但 CSAPP 是一本适合你在学习的任何阶段都抽空读上一点的书,读它总会有不同的收获。没把 CSAPP 放在这里不是说明它不重要。
我将把整套学习路径分为理论和应用两大类,并尽可能让书单安排的顺序循序渐进,比如计组的书单会安排在操作系统前面。但有一些类别的知识并不互相构成前置关系,比如前端和后端就不存在哪个为前置,这时它们的安排顺序就是随机的。但对于任何涉及前置知识的书单,我都会将前置显式标明出来。
计算机导论

中文书名:计算机科学导论
英文书名:Foundations of Computer Science
前置知识:无
正如书名所示,这是一本“导论”书。几乎任何学校的计算机科学与技术专业都会在大一开设一门“计算机导论”。简单来说,导论就是大杂烩,把大多数计算机领域都简要给你介绍一遍,但都不深入。然而,在你有时间的情况下,学习一下导论是很有意义的。它可以帮助你很好地树立起系统的计算机概念,帮助你在后续各个领域的学习中很轻松地入门,而非在哪门课最初一段时间的学习中感到困惑。
不过,导论并不是你一定需要先学习的。如果你时间紧急,可以直接跳过导论,我建议你可以直接开始数据结构与算法的学习。另外,导论中确实有一些部分不是你必须要阅读的,例如社交媒体导论,在多数情况下就是不需要阅读的。但我还是建议有时间尽可能读一遍,这花不了太多时间,而且会给你带来宝贵的计算机领域“常识”。
数据结构与算法

中文书名:算法
英文书名:Algorithms
前置知识:任何一门编程语言的基础知识
正如其朴素的书名一样,这是一本算法书——一本完完全全的“算法”书,而不是“算法分析与设计”这些课程的参考书。
说到算法,我相信很多人第一个想到的书是 MIT 的《算法导论》。不得不承认,直到今天《算法导论》仍是算法分析领域最权威的参考书之一。注意到了吗?我说的是“算法分析”而非“算法”,实际上前者更偏向一门数学课,会涉及较多的时间复杂度分析,而后者则偏向实用。而在当前,除非你需要做一些颇为深入的理论研究,否则学习“算法分析”并不能给你太多实际的帮助。各大公司所谓“算法知识”,要求的自然也是“数据结构与算法”方面的知识。而这里推荐的《算法》被公认为数据结构与算法最好的入门书之一,也有很多人认为这是当前最好的算法入门书。
本书演示算法使用的语言是 Java——这可能令一些人感到意外和疑虑。“如果我从未学过 Java,是不是说我需要先学点 Java 才能看这本书?”,我的回答是,这本书几乎不需要你拥有 Java 基础,也许仅需的一点基础你在类似菜鸟教程这样的网站上简单翻阅前面的一点内容就够了。
为了照顾到使用其他语言的读者,这本书尽可能使用了一个有些特别的封装库,使代码看起来更易懂、更通用,例如使用自定义的标准输入/输出类“StdIn/StdOut”来代替 Java 中的 Scanner 与 System.out,并且很少使用较为深入的 Java 特性,我认为只要读者有部分 C/C++甚至 Python 基础,要理解其中的代码都不会有很大难度。甚至即使你不打算学习任何有关 Java 的知识,直接开始阅读这本书,前面也花了一些篇幅大致介绍了 Java 语言,如果你打算直接靠这些较少的介绍入门 Java 可能会稍显吃力,但应该也大致足够。
其实我个人认为可以不考虑安装书中提供的自定义标准库,毕竟对于初学者而言,这可能也是一个劝退要素之一。考虑到 Java 环境的配置本来就不那么容易,还要引入一个标准库或许更容易让初学者头晕。因此,我建议直接使用 Java 内置的 Scanner 和 System.out 代替书中的 StdIn 和 StdOut,你可以很容易找到如何使用它们。然而,书中使用的绘图库就不太容易使用原生 Java 直接模拟了,但考虑到绘图本身只是帮助理解,而非绝对必要的,你可以跳过那些要求你使用绘图库完成的习题。
也可能有人会问,“为什么《算法》不像《算法导论》一样使用伪代码描述?”,这是因为本书希望书中的所有代码都是可运行的,读者也可以根据课后习题编写可以实际调试运行的代码。如果你确实不喜欢 Java,也可以尝试着用自己喜欢的语言,例如 C++或是 Python,重写书中的一些代码并完成课后习题,几乎所有习题都不对你具体使用哪门编程语言有限制。书中的代码都非常容易看懂,且附以大量用于描述的图片和表格,即使你一行实际的 Java 代码也不曾实际运行过,想看明白书中描述的各种数据结构和算法也是非常轻松的。你也可以很容易找到本书使用各种语言重写的代码和课后习题参考。读过这本书的人很多,资料自然也非常多。顺带一提,如果你不打算学习 Java,那么第一章中的大部分内容可以跳着读。
另外,需要指出的一点是,算法的学习并非读完一本书就足够了。更重要的是时常去做相关习题,例如在 LeetCode 上刷算法题。读书只是开了个头,想指望靠读本书就通过各大公司的面试是不可能的。想要熟练掌握算法,不刷题肯定还是不行的。
数据库

中文书名:数据库系统概念
英文书名:Database System Concepts
前置知识:数据结构与算法
数据库知识的学习并没有太多前置知识要求,将对数据库的学习放在学完数据结构与算法后的任何阶段都是合适的。有些学习路径会将其安排在学完操作系统之后,这也没什么问题。本书单将数据库安排在算法之后的原因是,下面对计算机硬件知识的学习涉及的算法知识较少,但算法学习非常需要巩固,直接开始学习硬件很容易忘记刚学习的很多算法知识。而数据库这门课程对算法知识的涉及很多(尤其是在文件系统与查询处理部分),学习数据库的过程中也能顺带巩固一下算法知识,因此这么安排。
数据库领域没有太多读物可以推荐,基本上入门数据库都推荐这本《数据库系统概念》,本书单也遵循传统,推荐这本巨著。书中不仅包含了最基本的 SQL 语句、ER 图设计、数据库范式、文件系统、查询处理与查询优化、并发与恢复等一切数据库课程都应该教授的基本内容,也包括了一些实用且前沿的内容,例如关系式数据库、分布式数据库、大数据与数据挖掘。在云原生的时代,《数据库系统概念》也与时俱进,毫不过时。然而,正如下面会提到的,如果你对书中一些前沿的内容不感兴趣,只想学点最通用的基础,那么可以考虑选购“本科教学版”。
在我个人看来,将对数据库的学习安排在前面还有个好处,就是能够与之后对操作系统知识的学习产生共鸣。在文件系统和索引部分,你可以了解到数据库如何为了提高文件读写性能而绞尽脑汁,而其中的一些思路与操作系统所采用的一些通用解决方法不谋而合。同理,在阅读事务管理部分时,你可以看到数据库如何为了保证原子性而考虑并发控制(锁),而操作系统的并发部分与之非常相似。而如果你第一次接触这些概念是在学习操作系统而非数据库时,也许会因为缺少一些更为实际的例子而难以体会这些概念的重要性。
值得注意的是,《数据库系统概念》这本书中文版将近 800 页,如果你感到自己很难啃下这么厚一本书,或许可以考虑阅读“本科教学版”,它只有不到 500 页。“本科教学版”相比原版删去了关系代数、复杂数据类型、大数据与数据分析、RAID、并行与分布式这几个部分,如果你对大数据与分布式数据库不那么感兴趣,“本科教学版”应当也足以提供完全足够的基础知识了。这里当然推荐你尽可能阅读原版,毕竟“本科教学版”删去的东西在如今看来还是相当重要的,然而如果你抱着一些功利性的目的学习数据库,例如打算通过面试或参加考试,不得不承认“本科教学版”也完全足够了。
数字电路&计算机系统

中文书名:计算机系统概论
英文书名:Introduction to Computing Systems: From Bits & Gates to C & Beyond
前置知识:无
有人可能会疑惑我为什么不直接推荐学习计组,而是要插入这样一本看起来有些奇怪的书。我们通常认为学习计组是正式学习计算机理论的开始,但是我既没有选择经典的 CSAPP 前四章,也没有选择广受好评的《计算机组成与设计 硬件/软件接口》,而是选择了耶鲁大学的这本《计算机系统概论》。
这并非一本严格意义上的计组教材,尽管它和计组有一些关系,但这也不是一本能从标题看出其和计组有关的书。光看中文翻译,甚至可能产生这本书和操作系统有关的误解,只有当看到副标题“From Bits & Gates to C & Beyond”,你才会明白这是一本从二进制和逻辑门开始,一步步介绍计算机中程序运行逻辑的书。这本书集中于一步步带你了解计算机如何真正运行一段程序,因此许多和主题不那么相关的东西,比如 SRAM 和 DRAM 的区分,就不在这本书的讨论范围内了,因此这不是一本能代替计组教材的书。
选择这本书作为开始似乎有些多余,然而当我真正翻开这本书时,我才意识到一开始阅读这样一本书是多么重要。我想如果我在大一时就看过这本书,即使只看过一小半,应当也能受益匪浅。
我推荐它的一大原因是因为这本书几乎没有阅读门槛,非常适合通过这本书先一步对整个计算机理论体系进行一遍梳理。和计组有关的很多书都会大量涉及汇编或 C 语言,但却很少有篇幅认真介绍它们,这导致这些书往往需要一定的前置知识才容易看懂。而且,这些相关书籍中对 C 语言的要求不止局限于能够掌握基本语法,还要求对系统级 API 调用有一定了解,直接硬读这些书籍中的 C 语言代码,容易一知半解。而《计算机系统概论》这本书不同,它在大致介绍完硬件基础后,用了很大篇幅介绍 C 语言,更难能可贵的是这部分对 C 语言的介绍和前面讲解的硬件基础直接关联到一起,这种讲解方式让人很有启发。
你可能会认为需要一本如《C Primer Plus》或《C++ Primer》这样的书帮助你详细地了解 C/C++的相关知识。然而如果你未来不打算从事相关工作,读这些书很容易让你过度陷入语言细节。如果你的目的仅仅是使用 C/C++辅助理解计算机理论方面的知识,那么这本书应当已经完全足够了。
如果你有能力或者有挑战自己的想法,那么你可以尝试阅读这本书的第三版英文版,第三版出版于 2019 年,相比起 2008 年的第二版要新得多。你可以直接在国内买到英文版,也可以很容易地自行找到英文版电子版。不过对于大多数人来说,现在已有中文版的第二版也不算太过时,毕竟理论知识的过时速度非常缓慢(1984 年的 SICP 还被奉为当代经典呢),如果你还是更偏爱中文版,稍旧一些的中文版第二版也可以阅读。
另外,你可能会对本书单中不包括模拟电路相关知识感到困惑。然而我实在不认为模拟电路对计算机理论的学习有多么重要,例如我很难想象在什么情况下学习计算机理论需要了解三极管原理,在我看来学习硬件知识止步于明白三极管/MOS 管可以起到开关作用就足够了,我们至多在逻辑门层面花些精力,将精力深入到晶体管层面就有些不太划算了。当然,这也仅仅是我的个人见解,如果你有异议,我也尊重你的意见,只是本书单确实就是按照我的想法安排的。
计算机组成原理

中文书名:计算机组成与设计 硬件/软件接口
英文书名:Computer Organization and Design: The Hardware/Software Interface
前置知识:数字电路
事实上,在阅读完上面一本书后,学习计组对你实际编程能力的提升就不大了。我们常说计组很重要,说得其实严格来说不是计组,而是上面一本书中的计算机系统。只是通常来说,大部分人学习计算机系统都是通过学习计组搞明白的,而且几乎所有的计组资料中也包含了计算机系统的知识,因此我们通常将这两个概念混为一谈。
如果你不经常和底层硬件打交道,比如做驱动、设备协议这一块,你可能并不是很需要学习这方面的知识。上一本书中学到的所有知识已经足够你进入下一个学习阶段,也就是操作系统的学习了。毕竟在操作系统涉及的系统级编程中,你也只需要考虑寄存器、cache、主存等概念,不需要考虑计组中会涉及的诸如 SRAM 和 DRAM 的硬件区分等问题——你只需要了解 cache 比主存更快就可以了,至于它们的硬件逻辑,又和你有什么关系呢?然而问题在于,很多人学习计组是为了应付一些更现实的问题,比如本科课程考试或研究生考试,这种情况下你就不得不学习一些更深入的计组知识了。
在这里我推荐经典的《计算机组成与设计 硬件/软件接口》。在计组领域,这应当是最经典的作品之一了(实际上,计算机体系结构的一些知识也为这本书的厚度做出了很大贡献,这并不是一本纯粹教授计组的书),在国内这更是首选。如果你正在学习计组相关的课程,不妨买一本拿来参考一下,或许比你的老师讲得更好。
值得注意的是,这本书很大一部分内容和上一本书有重叠。如果你已经阅读了上面一本书(如果你确实在跟着本书单走,也应该把上面一本书读完了),那么这本书的大多数内容可以略读,你只需要认真阅读那些让你感到陌生的知识。
至于大家推荐的 CSAPP,我仍考虑将它当作论外处理,建议作为一本“有时间翻翻”的读物,毕竟这本书究竟要归类到哪个领域我至今还难以下定论。有人说 CSAPP 的前四章也很适合作为计组教材,确实如此,但我想还是在这里放一本更专注于计组的书更加合适一些。
操作系统

中文书名:操作系统导论
英文书名:Operating Systems: Three Easy Pieces
前置知识:数据结构与算法;计算机硬件基本知识;C 语言基本知识(至少应当了解至结构体与指针)
谈到操作系统,也许有人会推荐《现代操作系统》,然而当前这本更新的《操作系统导论》(一般简称 os-tep)会更好。考虑到《现代操作系统》深受逆天翻译毒害,导致中文版好多句子读起来非常别扭,相比之下《操作系统导论》是更为合适的选择(尽管翻译得也比较生硬,但总体可读,至少比《现代操作系统》好上很多)。顺带一提,个人认为邮电的《操作系统导论》的排版印刷质量也比属于机工社黑皮书系列的《现代操作系统》好上一些,纸张更好,字体更大,读起来不那么累,虽然稍贵一些,但阅读体验其实更好。
作为本就不容易过时的理论书籍,这本英文原版出版于 2018 年的书在一众理论书籍中看起来几乎崭新。你可以感受到作者尽可能让学习操作系统变得轻松。书名副标题的”Three Easy Parts”分别指虚拟化、并发与持久性。书中穿插着一些有趣的扩展知识和一些有趣的对话,使阅读体验相对轻松不少。课后作业以给出可运行 Python 脚本的形式指导读者通过探索式的方法学习,非常生动有趣。除此之外,书后也包含几个大项目可以练手,和大多数西方的专业计算机教材一样,配套资料非常齐全。
PS:这本书的很大一部分课后作业(编码)需要在 UNIX 环境下进行,其中绝大部分可以在 Linux/Mac OS 环境下进行,少部分可以在 Windows 环境下进行。个人建议,如果使用 Windows 平台,可以考虑直接使用 WSL2 进行模拟,现在 WSL2 支持直接调用 Windows 本地的 VSCode 写代码,使用起来应当不很困难,至少比装个 Linux/Unix 更简单。另外,有一些用于演示的课后作业使用 Python 2 编写,如果你使用 Python 3,需要将其中的 print 语句进行修改才能正常运行。具体来说,就是将print 'a'改成print(),将print ''改成print(),print 'a',改成print('a', end=''),基本上这样就可以运行了。
计算机网络

中文书名:计算机网络 自顶向下方法
英文书名:Computer Network: A Top-Down Approach
前置知识:数据结构与算法;一些最基本的 Python 语法(如果你不想了解,也可以直接跳过书中极少数涉及 Python 代码的部分,但 Python 非常简单,学一点基础花不了你半个小时,所以为什么不学一学呢)
计算机网络是个较大的话题,在计算机的许多热门领域(如 Web 开发)中,计算机网络的重要性似乎也仅次于数据结构与算法。针对不同的需求,对于“计算机网络”这一主题也有许多不同的书籍推荐。然而为贯彻本书单的宗旨,这里还是只主要推荐一本书,即《计算机网络——自顶向下方法》。
“自顶向下”不是一个很常见的词语,但即使你从未见过它,也应该能够很容易地从字面意思猜出它的含义——从最顶层一步步讲解到底层知识。很多人第一次接触“自顶向下”这个词语可能就是在看到这本书的标题时。“自顶向下”听起来有些与众不同,毕竟在其他理论的教学上,往往遵从着“自底向上”的教学方式,如经典的“数字电路—计算机硬件—操作系统”路径。然而在计算机网络知识的教学上,“自顶向下”是个很不错的教学方法,读者并不从枯燥的硬件知识学起,而是一开始接触应用层(HTTP),然后一步步学到硬件知识,这可以更直观有趣地学习计算机网络的知识。
如果你觉得《自顶向下方法》这本书太过硬核(其实你不该这么觉得,毕竟本书单之前每本书都比它厚或差不多厚),你可以考虑读一读一本小书《网络是怎样连接的》,相比之下这本书更有趣味性一些,且基本也是按自顶向下的方式叙述的(严格来说是按照端到端的方式)。然而既然《网络是怎样连接的》这本书在本书单中被提到了,你也应该清楚这不是什么“通俗科普读物”,其实也是带点硬核的,读起来仍需要一些耐心。
其他
也许有人会发觉这里似乎缺少了一些重要的东西。编译原理去哪了?算法分析去哪了?这些当然是重要的。然而就我看来,这些并非是每个人都必要学习的。例如编译原理作为本科教学阶段综合难度最大的一门课,却是所有课程中实用度最低的一门,你也几乎很难在编译器开发之外的场景下找到它的应用,因而我不会在这里推荐任何有关的书籍。同理,算法分析甚至计算理论和信息论也是重要的,但我想除了理论计算机科学家,少有人需要掌握这方面的知识,因此我也不会在这些领域给出任何推荐书籍。
另一门重要的课是离散数学,然而本书单并未包含离散数学相关的书籍推荐。虽然很多时候讨论经典计算机理论课程只提到计组、计网、操作系统、数据结构、数据库这些课程,然而离散数学的重要性丝毫不亚于这些课程。在西方的传统离散数学教学中,会涉及数理逻辑、集合论、图论相关的知识(在中国,离散数学还通常包含简单抽象代数),而这些知识无论是对于数字电路、数据结构抑或是计算理论的学习都是很有帮助的。然而本书单并未包含离散数学的原因之一是其中很多内容已经渗入了各类课程中,例如许多经典的算法教材已经包含了本应在离散数学中学习的生成树概念,而数理逻辑的部分内容也常常被数字电路甚至计算机导论所包含,因此已经不很需要单独学习一门离散数学了。另一点是离散数学中大部分内容实际上是计算理论等课程的前置,而这些课程已经在本书单中删去,因此本书单自然也就不包含离散数学了。
如果有读者认为有必要打好一些离散数学的基础,那么可以考虑阅读左孝凌的《离散数学》,这是一本非常简洁的书,几乎用最少的语言较为清晰地描述了离散数学至少该教授的内容,唯一的缺点是出版时间距今已四十多年了,有些陈旧。有人会批评左孝凌这本书不够生动,但能在这么薄一本书中塞下能够让人读懂的解释与合适的习题,其实也实属不易了,不用过度苛求。另外,有些人可能会推荐 Rosen 的《离散数学及其应用》,我个人不推荐这本,这本书的厚度远远超出了学习基础理论知识所需要掌握的,更像一本“百科”而非入门书。
也许还有人会提到 SICP——即《计算机程序的构造和解释》。这是一本非常经典的书,其中也有非常精妙的思想,你或许可以考虑在抽象能力遇到瓶颈时读一读。然而这本书不应该包含在这里提到的计算机理论学习路径中,也绝非必要的。我很难描述这是一本怎么样的书,我读完后只能告诉他人这是一本关于“抽象”这一概念本身的书,至于这本书具体讲了些什么,以我贫乏的表达能力很难总结出来。总之,SICP 应当是一本在你对编程有了一些深入思考后阅读的书。有很多人将这本书抬到了不属于它的过高地位,经典的就是拿 MIT 拿这本书作为计算机科学专业第一门课举例,然而要知道 MIT 也早就将计算机科学专业的第一门课换成 Python,并且把 SICP 作为选修课了。SICP 绝对不适合作为一本入门书。它很重要,但它的重要性非常特别,它既没有被高估也没有被低估,但总有人将它放到不属于它的位置,这很不合适。至少就本书单来说,读者不需要考虑任何有关 SICP 的事,当前你只需要了解有这样一本书就可以了。
最后,还记得 CSAPP 吗?这可能是这里提到的所有书中最值得读的,但没有直接包含在本书单中,因为这本书涉及领域太广,实在是难以归类。显然,书单中没有直接包含 CSAPP 并不意味着它不值得读,相反,你其实可以在任何时候翻开 CSAPP 读一读,甚至在你已经学完这里提到的所有理论知识后,CSAPP 也常常给人新的启发。
本书单的宗旨是总结出严格的“一条”路径。这条路径是循序渐进的、易于理解的,同时也应当是适用于绝大多数人的。本书单必然不可能照顾到少数领域的需求,例如量子计算领域、人工智能领域等。本书单只总结计算机这个大领域中几乎所有人都有必要掌握的知识。如果我像其他书单一样每个领域都推荐好几本书甚至十几本书,那恐怕只能让多数读者感到眼花缭乱。