原文链接 作者:Matthew Might 1 译者:Reion Chan 2
随着现代计算机科学领域的飞速发展,辨别哪些属于该领域范畴变得越来越具挑战性。我隶属的学院也正参与到这场争论之中,由此我把自己的一些想法总结成册,以此来回答一个问题:“每个从事计算机科学专业都应该知道什么?”
我试图结合如下四个要点来回答这个问题:
- 每个学生应该知道哪些东西才能找到一份好工作?
- 每个学生应该知道哪些东西才能维持自身职业生涯?
- 每个学生应该知道哪些东西才能进入研究生院?
- 每个学生应该知道哪些东西才能造福社会?
我下面的想法即有通用准则也有具体建议两方面因素,来描绘现代计算领域。从事计算机科学专业,可将此作为免费的自学指导。如有增加或删除建议,请发 电子邮件 或 Tweet 。
作品集与简历
计算机科学从工程学与数学分离后,都采取简历的方式来招聘毕业生。
一份简历说明不了程序员的能力。每个计算机专业的学生都应该建立一个作品集。
作品集可以是简单的个人技术博客,每篇文章介绍展示自己的一些项目经验及所取得的成就。当然,如果每个项目的页面包含可公开浏览的项目源代码(可托管在类似 GitHub 或 Goole Code),那将是更好的作品集。且应该给出对开源软件所做的贡献的具体链接。代码作品集能够给雇主一个直观的能力判断依据,而这是平均学分绩点及简历所不能及的。教师们应该设计一些课程项目来深化作品集的效用,而学生应该在每门课程结束时花些时间来更新它们。
示例:
技术交流
计算机科学中独狼是一种濒危的物种。
现代计算机科学家必须具备将他们的想法传达给非程序员的语言能力。
在小公司,程序员是否能将自己的想法传达给管理层将可能影响公司的成败。不幸的是,这并不一定是一堂单一的课程(虽然有这么一堂详实的技术交流课程也无伤大雅)。更多的课程需要给学生提供一个展示他们作品的机会,并让他们能通过口头表述自己的想法。
-
具体建议
我们会建议学生掌握一个演示工具,例如:PowerPoint 或 Keynote。仅使用 LaTeX 来演示相比前两者就显得过分静态单调,当然对于需要制作精美的数学文档,LaTeX 在这方面优势明显,技术课程的书面作业都应该以 LaTeX 的形式提交。 -
推荐阅读
- Writing for Computer Science Zobel 著
- Even a Geek Can Speak Asher 著
- The LaTeX Companion
- The TeXbook Knuth 著 (只推荐给这方面的专家)
- Notes on Mathematical Writing
- Simon Peyton-Jones 的建议 How to Give a Good Research Talk
- My advice on how to send and reply to email
工程核心
计算机科学并非彻头彻尾的工程学,但已足够接近了。
计算机的科学家将会发现自己常与工程师们一起工作。计算机科学家们和传统意义上的工程师们需要将相同的语言—— 一个源于实分析、线性代数、概率学以及物理学的语言。
计算机科学家应该通过电磁学来掌握物理学。但是,要做到这些的前提,他们将还要掌握多元微积分(有时为了更好的衡量还需了解微分方程),在构建声音模拟方面,概率学和线性代数的指令通常弥足珍贵。在解读结果方面,良好的统计学基础将无可替代。
- 推荐阅读
- 微积分 Spivak 著
- All of Statistics: A Concise Course in Statistical Inference Wasserman 著
Unix 哲学
计算机科学家们应该熟练的掌握及运用 Unix 计算哲学。
Unix 哲学(与 Unix 本身相反)着重语言的抽象与组合,以此来影响计算本身。实践中,这通常意味着熟练的使用命令行计算、文本式的配置及非 IDE 式的软件开发。
-
具体建议
面对一些流行的 Unix 系统,现今的计算机科学家们应该熟练的掌握 Unix 一些基础,包含如下能力:- 浏览及操作文件系统
- 管道式的组合操作
- 熟练的使用 emacs 和 vim 进行文件的编辑
- 编写一些简单的 shell 脚本
除非很了解 Unix 哲学的强大之处,否则学生们很容易抵触这门学问。因此,最好的方式是让学生们使用 Unix 去完成一些对于 Unix 来说独具优势的实用任务,例如:
- 找出给定目录下占用空间最多的前五个文件
- 通过文件内容而非文件名的方式找出重复的 Mp3 文件
- 把姓或名的首字母为小写的名字列出来,并尝试改正它
- 找出所有 第二个字母是 x 倒数第二个字母是 n 的所有英文单词
- 直接将电脑麦克风的输入信息通过网络在另一台电脑音箱中播放
- 把给定目录下文件名包含空格的全部替换为下划线
- 报告最近十次错误访问 web 服务器的具体客户端 IP 地址
-
推荐阅读
- The Unix Programming Environment Kernighan、Pike 著
- The Linux Programming Interface: A Linux and UNIX System Programming Handbook Kerrisk 著
- Unix Power Tools Powers, Peek, O’Reilly、Loukides 著
- commandlinefu
- Linux Server Hacks
- The single Unix specification
系统管理
一些计算机科学家们戏谑的将系统管理称作 “IT”任务。 逻辑是计算机科学家可以教他们自己一切技术人员能做所有事情。
理论上,这是对的。但这常常带来误导:计算机科学家们一定能彻底地、安全地管理他们自己的系统及网络。很多软件开发相关的任务在没有提交给系统管理员时大多能高效的执行。
- 具体建议
每个现代计算机科学家都应该:
- 安装和管理一个 Linux 发布版操作系统
- 配置及编译一个 Linux 内核
- 使用 dig、ping、traceroute 来定位发现一个连接疑难问题
- 编译及配置一个 web 服务器,例如:apache
- 编译及配置一个 DNS 后台进程,例如:bind
- 通过一个文本编辑器来维护一个 web 网站
- 网线的接法
- 推荐阅读
- UNIX and Linux System Administration Handbook Nemeth, Synder, Hein and Whaley 著
编程语言
编程语言起伏遵循太阳循环规律。
开发人员的职业生涯则不应该。根据市场需求来教授一门相应的语言确实非常重要,而学生如何通过自学来掌握一门新的语言同样重要。最佳的学习编程语言的方式是学习多种编程语言和编程范式。
学习另一门新语言的困难度是你前一门已掌握语言困难度的一半。此外,为了正真的掌握一门编程语言,必须去实现该门语言。理想情况下,每个计算机专业的都应该学习编译学,至少,应该实现一个解释器。
- 具体语言
下面的每种语言都给出了一个合理的实际运用混合范式的典范:
- Racket
- C
- JavaScript
- Squeak
- Java
- Standard ML
- Prolog
- Scala
- Haskell
- C++
- Assembly
-
Racket
作为一个包含 Lisp 全部特性的方言版,它有着激进的简化的语法。 对一部分学生而言,该语法是一个障碍。
坦率的讲,如果对于暂时性的接受一个不同语法的语言都有思想包袱的学生,他们是缺乏使自己能够在在计算机科学生涯幸存下来的思想灵活性的。
Racket 是个强大的宏系统,它还是一个通过消除数据与代码边界来为高阶编程带来便捷的工具。如果教法得当,可完全替代 Lisp 。- 推荐阅读
- How to Design Programs Felleisen, Findler, Flatt , Krishnamurthi 著
- The Racket Docs
- 推荐阅读
-
ANSI C
C 是对硅的简洁且不可“饶恕”的抽象
C 在嵌入式系统编程中毫无对手,通过学习 C 的方式来传达对冯诺依曼架构更深层次的理解的优势,是别的语言所不具备的。
介于不严谨的使用 C 语言编程导致其与流行的缓存区溢出类病毒密切相关,程序员如何正确的使用 C 语言进行编程成为当务之急。- 推荐阅读
- ANSI C Kernighan and Ritchie 著
- 推荐阅读
-
JavaScript
JavaScript 是流行的动态的、高阶语言语义模型的代表,像这样的还有 Python、Ruby 和 Perl。作为 Web 领域的母语,它的实用性是独一无二的。- 推荐阅读
- JavaScript: The Definitive Guide Flanagan 著
- JavaScript: The Good Parts Crockford 著
- Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript Herman 著
- 推荐阅读
-
Squeak
Squeak 是一个现代的 Smalltalk 的方言,纯面向对象的语言。 它诠释出面向对象的精髓。 -
Java
Java 将仍保持流行很长时间以致于你不可能忽视它。- 推荐阅读
- Effective Java Bloch 著 (鄙人的快读笔记)
- 推荐阅读
-
Standard ML
Standard ML 是一个彻底的 Hindley-Milner 系统的具象。 Hindley-Milner 类型系统是现代计算学伟大的成就之一。
虽然是指数级的复杂度,Hindley-Milner 系统的类型推断却总能快速服务于人们所感兴趣的程序。
类型系统足够丰富以致于允许复杂的结构化的不可变量的表达式。它足够的丰富,实际上,良好类型的程序经常是无 bug 的。- 推荐阅读
- ML for Working Programmer Paulson 著
- The Definition of Standard ML Milner, Harper, MacQueen,Tofte 著
- 推荐阅读
-
Prolog
虽然在应用中处在合适位置,逻辑编程是一个可选的范式应用于计算的思维当中。在程序员可能需要在另一个范式中仿真它的情况下,更好的理解逻辑编程是值得的。
另一个值得学习的逻辑语言是 miniKanren, 它是一个纯粹的逻辑编程。这种限制已经演化为一个可选的逻辑编程样式,被称作关系编程。它被赋予一些属性不是一般的 Prolog 程序所具备的。 -
Scala
Scala 是一个设计良好的融合了函数式编程及面向对象编程的语言。
Scala 是原本 Java 应该成为的样子。它构建在 Java 虚拟机之上,它兼容已存在的 Java 代码库,因此,它被认为最可能成为 Java的继任者。- 推荐阅读
- Programming in Scala Odersky, Spoon,Venners 著
- Programming Scala Wampler,Payne 著
- 推荐阅读
-
Haskell
Haskell 是 Hindley-Milner 家族语言皇冠上的明珠。 由于充分的利用懒加载机制, Haskell 在主流编程语言中最接近纯数学式编程的语言。- 推荐阅读
- Learn You a Haskell Lipovaca 著
- Real World Haskell O’Sullivan, Goerzen, Stewart 著
- 推荐阅读
-
ISO C++
C++ 是躲不开的魔障。
但是,自从它一定要被教,那么就必须教的完全。特别地,计算机专业的应该对元模板编程有所了解。- 推荐阅读
- The C++ Programming Language Stroustrup 著
- C++ Templates: The Complete Guide Vandevoorde and Josuttis 著
- Programming Pearls Bentley 著
- 推荐阅读
-
Assembly
任何汇编语言都行。自从 x86 流行以来,那最好就学它了。
学习编译器是最好的学习汇编的方式,因为它能使计算机科学家对高级语言如何转变而来有直观的认识。-
具体建议
计算机科学家应该理解生成式编程(宏)、词法范围、闭包、continuations、高阶函数、动态指派、分型、modules and functors、单子(Monads)作为不同于任何具体语法的词法概念。 -
推荐阅读
- Structure and Interpretation of Computer Programs Abelson, Sussman, Sussman 著
- Lisp in Small Pieces Queinnec 著
-
离散数学
计算机科学家必须牢固掌握形式逻辑证明(formal logic and of proof)。通过代数操作证明和自然演算法来推理常规的编程任务,通过归纳法来推理递归函数的构造。
计算机科学家必须精通形式的数学符号,严密地演算基本的离散结构:集合、元组、序列、函数及幂集。
- 具体建议
对于计算机科学家来说,涵盖以下方面的推理很重要:
- 树
- 图
- 形式语言
- 自动机 automata
学生应该学习数论知识去研究实现常见的加密协议
- 推荐阅读
- How to Prove It: A Structured Approach Velleman 著
- How To Solve It Polya 著
数据结构与算法
学生们应该看到常见的数据结构和算法。 但是比知道特定算法或数据结构更重要的是,计算机科学家必须知道如何设计算法(如:贪心法、动态策略算法)和如何跨越理论上的算法和具体算法实现之间的鸿沟。
-
具体建议
起码,计算机专业的想要保持自身职业生涯的优势,就应该了解下面所列举的:- 哈希表
- 链表
- 树
- 二分查找树
- 有向图 和 无向图
计算机科学家们应该准备去实现扩展作用在上面的数据结构上算法,包括具备增、删、查找元素的能力。为了完整起见,计算机科学家们应该知道每种算法的已实现及功能性的版本。
- 推荐阅读
- CLRS
- Art of Computer Programming 的每一期 Knuth 著
理论
对理论的把握是在研究生院从事研究的先决条件。
当理论能提供一个关于问题边界的阐述(或当它提供了一个规避起初看起来比较困难问题边界的方法),此时理论的作用是无价的。计算复杂度可以被称作在所有计算机科学领域少数的真实预测理论。计算机科学家必须知道易处理性和可计算性的边界在哪里,忽略了这些限制将导致挫折,甚至是彻底失败。
-
具体建议
在本科学习阶段,理论将至少要涵盖计算模型及计算复杂度。 计算模型应该涵盖有限状态自动机(finite-state automata)、常规语言(常规表达式)、下推自动机(pushdown automata)、无上下文语言、形式语法(formal grammars)、图灵机、λ 演算、不可断定性。
在本科学习阶段,学生应该至少应该具备足够的复杂度知识,从而理解 P、NP、NP-Hard 以及 NP-Complete 之间的区别。为了避免留下错的印象,学生应该通过简化的 SAT 和 使用现代的 SAT 求解器 来解决一些 NP 中的大问题。 -
推荐阅读
- Introduction to the Theory of Computation Sipser 著
- Computational Complexity Papadimitriou 著
- Algorithms Sedgewick, Wayne 著
- Introduction to Algorithms Cormen, Leiserson, Rivest, Stein 著
架构
没有什么能够替代扎实的理解计算机架构。
计算机科学家们应该从晶体管级别及之上来理解当代计算机。
理解架构应该围绕着抽象的标准等级:晶体管、门电路、累加器、复用器、触发器、算术逻辑单元、控制单元、缓存及随机存取存储器。理解 GPU 的高性能计算模型将会是不久的将来一个重要课题。译者注:现在已经很重要了 :-)
-
具体建议
很好的理解 缓存、总线、硬件内存管理是达成高性能的当代系统的必要条件。 为了更好的掌握机器架构,学生应该设计及仿真一个小的 CPU -
推荐阅读
- nand2tetris, 从零开始构建一台计算机 (鄙人文章:Nand2Tetris 项目介绍)
- Computer Organization and Design Patterson and Hennessy 著
- “What every programmer should know about memory” Drepper 著
操作系统
任何一个足够大大的程序最终都会演变成一个操作系统。
为此,计算机科学家们应该清楚内核是如何处理系统调用、分页、进程调度、上下文切换、文件系统及内部资源管理。
为了达到高性能,较好的理解操作系统的重要性仅次于去理解编译器及架构。当在嵌入式编程的情况下,理解操作系统(更宽泛的讲为 运行时系统)极其重要,没有之一。
- 具体建议
在真实的操作系统中进行各种实践是非常重要的,借助于 Linux 和 虚拟机,这项工作变得比以前容易了。为了更好的理解内核,学生应该:
- 在启动程序中打印一个 “Hello world”
- 设计一个他们自己的调度器
- 修改一个分页处理策略
- 创建他们自己的文件系统
- 推荐阅读
- Linux Kernel Development Love 著
网络
由于无处不在的网络,计算机科学家应该加强理解网络中的网络栈及路由协议。
在一个不可靠的传输协议层(如:IP)之上创建一个有效率的、可靠的传输协议(如:TCP)的原理对于计算机科学工作者来说不应该感觉很神奇。 它应该是一个核心的知识点。
计算机科学家必须懂得在协议设计中的各种权衡。例如,何时选择 TCP 何时选择 UDP。程序员同样也需要了解在大级别范围下使用 UDP 所造成的拥塞的社会启示。
- 详细建议
介于现代程序员经常需要遭遇网络编程,很有必要去了解下面这些已经存在的协议标准:
- 802.3 和 802.11
- IPv4 和 IPv6
- DNS, SMTP 以及 HTTP
计算机科学家们应该知道在解决包冲突方案中的指数回退和拥塞控制中的加法增加及乘法减少机制。每个计算机科学家都应该实现以下内容:
- HTTP 客户端和后台服务进程
- DNS 解析器及服务器
- 命令行方式的 SMTP 邮件服务
没有使用过 wireshark 去嗅一嗅他们导师的 Google 查询记录,甚至都不应该让他们通过相关网络课程。要求所有的学生都去基于原始的 IP 上构建一个可信赖的网络传输协议的想法可能不大现实,但是,对当时还是学生的我来说,它对我自身经验提升有着很大帮助。
- 推荐阅读
- Unix Network Programming Stevens, Fenner, Rudoff 著
安全
大多数的安全病毒都来至于不严谨的编程这是一个不幸的事实。更不幸的事实是大多数学校在教授程序员如何写出安全的代码方面做得很糟糕。
作为计算机科学家,必须对哪个程序能够稍作妥协有清醒的认识。他们需要开发出具有防御性编程的意识,需要考虑他们的代码可能遭受的攻击。安全是这样一种培训,它最好贯穿所有的课程:每个学科都应该警告学生它们固有的漏洞隐患。
- 具体建议
至少,每个计算机科学家需要了解:
- 社会工程学
- 缓冲溢出
- 整形溢出
- 代码注入漏洞
- 竞态条件
- 特权混淆
一些读者也指出,计算机工作者同样需要具有 IT 安全衡量意思,例如:如何选择合理的较好的密码,如何恰当的配置防火墙参数。
- 推荐阅读
- Metasploit: The Penetration Tester’s Guide Kennedy, O’Gorman, Kearns Aharoni 著
- Security Engineering Anderson 著
密码学
密码学让我们的数字生活成为可能。
计算机工作者应该掌握,且能够实现下面的一些理念,及其实现中的各种陷阱:
- 对称密钥密码体系
- 公钥密码体系
- 安全散列函数
- challenge-response 身份验证
- 数字签名算法
- 阈值密码体系
由于密码系统实现中的常见错误,因此每个计算机科学家都应该知道如何为手头的任务获取足够随机的随机数。至少,几乎每次数据泄露都显示,计算机科学家需要知道如何给密码的散列值加盐以增加安全性。
-
具体建议
每一个计算机科学家都应该乐于使用手动滚动的统计工具来破译使用近代密码系统的密文。RSA 是足够容易去实现的,故每个人都应该去实现它。每个学生应该创建他们自己的数据证书,在 Apache 中建立起 HTTPS 。学生同样应该写一个控制台版本的 web 客户端,其链接构建在 SSL 之上。
作为严谨且实际的问题,每个计算机工作者都应该知道如何使用 GPG,怎样使用公钥为 SSH 认证,怎样加密一个目录或者整个硬盘。
-
推荐阅读
- Cryptography Engineering Ferguson, Schneier, Kohno 著
软件测试
软件测试必须始终贯穿分布在整个课程当中。
软件工程的课程中能够涉及测试的基本样式,但没什么能够替代实践这门艺术。
应该针对学生们上交的测试用例进行评分。我使用学生提交的测试用例来对照其他一部分学生的。学生们貌似不是很重视开发防御性的测试用例,但当面临抵御他们同学的代码攻击时,他们发挥的很好。
用户体验设计
程序员经常为其他程序员编写软件,更甚者,为他们自己。
用户接口设计(或更宽泛地说,用户体验设计)或许在计算机科学家们中是最嗤之以鼻的。甚至在很多教授之中,都有这样的误解,认为用户体验是一个不能教授的“软”技能。据显示,现今的用户体验设计扎根于从人因工程及工业设计中汲取经验。
如不考虑其他,计算机科学家们应该知道,接口需要执行任务的难易程度与任务的频率及其重要性成正比。作为一种实用性,每个程序员都应该习惯在 HTML、CSS、JavaScript 中设计可用的 Web 界面。
- 推荐阅读
可视化
较好的可视化是以能够让人感知到它所传达出的信息的方式来呈现数据信息。做好这件事情其实并不容易。现今社会是一个数据的海洋,最大限度的去发掘人类的洞察力是可视化成为可行的关键所在。
- 推荐阅读
并行
并行又回来了,并且比以往更丑陋。
不幸的事实是要想驾驭好并行需要在架构方面有很深的造诣:多核、缓存、总线、GPU 和不断地练习,数不尽的练习……
-
具体建议
在回答并行编程是什么的时候其实没有很清晰明朗的所谓最终答案,但已经浮现出一些具体领域方面的解决方案了。现今,学生们应该去学习 CUDA 和 OpenCL 。线程是并行的最小抽象单位,尤其是当缓存及缓存相关的牵涉其中时。 但同时,线程又是即热门有棘手,故值得去学习。并行线程是一个更有理由去学习的轻便的线程库。
对于那些对大型并行感兴趣的人,学习 MPI 将是前提条件。说道理念方面,map-reduce 的模型似乎还是经久不衰。
软件工程
软件工程的原则变化之快与编程语言的变化不相伯仲。
在团队软件的构建实践中,一个良好的课程应该提供相关工作中常会出现的陷阱知识。经常被读者建议可以将学生分为三个团队,分别在不同的三个项目中轮流充当项目领导的角色。学习如何通过已有的大型代码库进行攻击及演练,对大多数的程序员而言是一个不得不掌握的技术,这方面最好在学校里面就应该学习掌握,而不要拖到工作之后。
-
具体建议
所有的学生都需要掌握中心版本控制系统,比如 SVN,和分布式版本控制系统,例如 Git 。像 gdb、valgrind 这样的调试工具相关的工作知识,在它们最终成为必须掌握的要点之前,还有很长的路要走。
-
推荐阅读
- Version Control by Example Sink 著
形式方法
随着对交付安全可靠的软件需求的增加,形式方法可能有一天成为实现这个需求的唯一手段。
目前,软件的形式建模及验证仍具挑战性,但是该领域还在稳步发展,随着时间 的更迭,相信要达到这个目地将越来越容易。在计算机科学家的职业生涯中,形式软件构建将会成为一项必要的技能的那天终将到来。每个计算机工作者都应该适当的使用一个定理证明。
学习使用定理证明会立刻影响编码风格。例如:它会使得编写 match、switch 方法声明的人本能的意识到其分支并没有涵盖所有的可能性。在写递归函数时,定理证明能够消除不确定性。
- 推荐阅读
图形及模拟
没有什么学科能比图像学更能通过“精巧”来获取主导权。
该领域正朝着更好的方向发展。为此,没有什么方式可以比使用图形及模拟来展现对精巧编程及优化工作的认识。我学习的一半以上的编码攻击都来自于我对图形的研究。
- 具体建议
检点的光线追踪器可以在100行以下的代码中构建。在计算出线框的 3D 引擎中执行透视 3D 投影所需的转换是令人身心愉悦的。像 BSP(二叉空间分割树)、z-buffer 渲染这样的数据结构及算法是精巧的设计。
在图形及模拟方面,下面还有更多的信息。
- 推荐阅读
机器人技术
机器人技术或许是编程入门介绍最吸引人的方式之一。
此外,随着机器人技术的成本不断下降,门槛的打开将会迎来私人机器人的革命。对于那些可编程,自身物理自动化程度极高的机器人即将到来。
人工智能
如果不是受计算历史早期的影响,计算机科学家们应该学习人工智能。 虽然智能机器的最初梦想似乎遥遥无期,但人工智能激发了许多实用领域,例如机器学习,数据挖掘和自然语言处理。
- 推荐阅读
- Artificial Intelligence Russell、Norvig 著
机器学习
除了出色的技术优势之外,多数的工作中都要求机器学习相关的工程师。 暗示着每个计算机科学家们都应该掌握机器学习的基本原理。再三强调,机器学习是需要掌握概率学及统计学方面的知识的。
-
具体建议
本科阶段,核心理念应该包含贝叶斯(Bayesian)网络,集群和决策树的学习。 -
推荐阅读
- Machine Learning Mitchell 著
数据库
数据库太普遍太常用,以致于经常被忽视了。
理解基础数据结构及算法如何赋予数据库引擎以力量是很有用处的,因为这将有助于程序员在大型软件系统中重新实现数据库系统。
关系代数和关系演算在亚图灵模型中是非常成功的例子。
不同于 UML 模型,ER 模型对于软件产品的设计及约束上似乎提供更加合理的 机制。
-
具体建议
相比较费劲心力重新建立运营自己的一套,能够建立和操作 LAMP 技术栈的计算机科学家会是个更好的主意。 -
推荐阅读
- SQL and Relational Theory Date 著