每个计算机科学专业都应该知道什么

1,177 阅读19分钟

本文是前一阵看到Matt Might的一篇博客What every computer science major should know觉得写的很好,于是想翻译成中文,自己收藏起来。

引言

计算机科学领域的迅速发展使得确定现代计算机科学学位应包含什么内容变得具有挑战性。我自己所在的院系也正在就此进行辩论,因此我综合了以下几点:

  • 每个学生都应该了解什么才能获得一份好工作?
  • 每个学生都应该了解什么才能保持终身就业?
  • 每个学生都应该了解什么才能进入研究生院?
  • 每个学生都应该了解什么才能造福社会?

我下面的想法既包括一般原则,也包括与现代计算环境相关的具体建议。

计算机科学专业的学生们:请随意将此作为自学指南。

请通过电子邮件或推特提出增加和删除的建议。

更新

感谢所有的建议和提醒!我将把它们整合进来,使这份文档保持活力。

作品集与简历

计算机科学专业源于工程学和数学,采取了基于简历的招聘方式。然而,简历无法展示程序员的能力。因此,每个计算机科学专业的学生都应该建立一个作品集。

什么是作品集?

作品集可以简单到个人博客,每个项目或成就都有一篇帖子。更好的作品集会包括每个项目的页面,并且代码可以公开浏览(可能托管在GitHub或Google Code上)。对开源的贡献应该被链接和记录。

为什么作品集重要?

作品集可以让雇主直接评估能力,而GPA和简历则不能。教授应该设计课程项目以便在作品集上留下深刻印象,学生在每门课程结束时,应该花时间更新它们。

示例

  • Edward Yang的网站
  • Michael Bradshaw的网站
  • GitHub是我的简历

技术交流

在计算机科学中,独行侠是濒危物种。现代计算机科学家必须能够有说服力地、清晰地向非程序员传达他们的想法。

为什么技术交流重要?

在小公司里,程序员能否向管理层传达她的想法可能决定公司的成败。这不是通过增加一门课程就能解决的问题,但一门扎实的技术交流课程很有帮助。

具体建议

我建议学生们掌握像PowerPoint或Keynote这样的演示工具。对于制作精美的数学文档,LaTeX无与伦比。所有技术课程的书面作业都应该用LaTeX提交。

推荐阅读

  • 《计算机科学写作》Zobel著
  • 《即使是极客也能演讲》Asher著
  • 《LaTeX伴侣》
  • 《TeXbook》Knuth著(警告:仅限专家)
  • 《数学写作笔记》
  • Simon Peyton-Jones关于如何进行优秀研究演讲的建议
  • 如何发送和回复电子邮件的建议

工程核心

计算机科学不完全是工程学,但它足够接近。计算机科学家会发现他们自己与工程师一起工作,需要说同一种语言——一种根植于实分析、线性代数、概率论和物理学的语言。

推荐阅读

  • 《微积分》Spivak著
  • 《统计学:简明统计推断课程》Wasserman著

Unix哲学

计算机科学家应该熟悉并实践Unix的计算哲学。Unix哲学(与Unix操作系统本身不同)强调通过语言抽象和组合来实现计算。

具体建议

鉴于Unix系统的普遍性,当今的计算机科学家应该熟练掌握基本的Unix操作,包括:

  • 浏览和操作文件系统
  • 使用管道组合进程
  • 使用emacs和vim舒适地编辑文件
  • 为软件项目创建、修改和执行Makefile
  • 编写简单的shell脚本

学生们除非理解Unix哲学的强大之处,否则会拒绝接受它。因此,最好通过让学生们完成那些Unix具有比较优势的有用任务来挑战他们,例如:

  • 找出给定目录中占用空间最多的五个文件夹
  • 报告计算机上按文件内容而非文件名重复的MP3
  • 将名单中所有名字和姓氏都小写的情况正确地大写
  • 找出所有以'x'为第二个字母,'n'为倒数第二个字母的英文单词
  • 直接将你的麦克风输入通过网络路由到另一台计算机的扬声器
  • 将给定目录中所有文件名中的空格替换为下划线
  • 报告来自特定IP地址的web服务器的最后十次异常访问

推荐阅读

  • 《Unix编程环境》Kernighan和Pike著
  • 《Linux编程接口:Linux和UNIX系统编程手册》Kerrisk著
  • 《Unix工具书》Powers, Peek, O'Reilly和Loukides著
  • commandlinefu
  • Linux Server Hacks
  • 单一Unix规范

系统管理

一些计算机科学家对系统管理不屑一顾,认为它是“IT”任务。理论上,计算机科学家可以自学技术人员能做的任何事情,但这种态度是错误的:计算机科学家必须能够胜任和安全地管理他们自己的系统和网络。

具体建议

每个现代计算机科学家都应该能够:

  • 安装和管理Linux发行版
  • 配置和编译Linux内核
  • 使用dig、ping和traceroute排除连接故障
  • 编译和配置像apache这样的web服务器
  • 编译和配置像bind这样的DNS守护进程
  • 用文本编辑器维护网站
  • 切割和压制网络电缆

推荐阅读

  • 《UNIX和Linux系统管理手册》Nemeth, Synder, Hein和Whaley著

编程语言

编程语言的流行随着时间而变化,但程序员的职业生涯不应该受此影响。教授与雇主相关的语言很重要,同样重要的是让学生学会如何自学新语言。

特定语言

  • Racket
  • ANSI C
  • JavaScript
  • Squeak
  • Java
  • Standard ML
  • Prolog
  • Scala
  • Haskell
  • C++
  • 汇编语言

每种语言都提供了不同的范式和实际应用的混合,学习多种语言有助于理解编程的本质。

推荐阅读

  • 《如何设计程序》Felleisen, Findler, Flatt和Krishnamurthi著
  • 《ANSI C》Kernighan和Ritchie著
  • 《JavaScript权威指南》Flanagan著
  • 《Effective Java》Bloch著
  • 《ML for the Working Programmer》Paulson著
  • 《Standard ML的定义》Milner, Harper, MacQueen和Tofte著
  • 《Learn Prolog Now!》
  • 《Programming in Scala》Odersky, Spoon和Venners著
  • 《Learn You a Haskell》Lipovaca著
  • 《Structure and Interpretation of Computer Programs》Abelson, Sussman和Sussman著
  • 《Lisp in Small Pieces》Queinnec著

离散数学

计算机科学家必须对形式逻辑和证明有深入的理解。掌握形式数学符号和对基本离散结构的严格推理至关重要。

具体建议

计算机科学家应该涵盖关于以下主题的推理:

  • 形式语言
  • 自动机

学生们应该学习足够的数论,以研究和实现常见的加密协议。

推荐阅读

  • 《How to Prove It: A Structured Approach》Velleman著
  • 《How To Solve It》Polya著

数据结构与算法

学生当然应该了解常见的(或者罕见但异常有效的)数据结构和算法。但是,比了解特定的算法或数据结构更重要的是,计算机科学家必须理解如何设计算法(例如,贪婪算法、动态规划策略),以及如何在理想中的算法与它的具体实现之间架起桥梁。

具体建议

至少,寻求长期稳定就业的计算机科学家应该了解以下所有内容:

  • 哈希表
  • 链表
  • 二叉搜索树
  • 有向和无向图

计算机科学家应该准备好实现或扩展在这些数据结构上操作的算法,包括搜索元素、添加元素和删除元素的能力。为了完整性,计算机科学家应该了解每种算法的命令式和函数式版本。

推荐阅读

  • 《算法导论》(CLRS)
  • Donald E. Knuth的《计算机程序设计艺术》系列

理论

掌握理论是研究生阶段研究的先决条件。理论在提供问题的硬性界限(或提供绕过最初看似硬性界限的手段)时是无价的。计算复杂性可以正当地声称是计算机“科学”中少数真正具有预测性的理论之一。

具体建议

在本科水平上,理论至少应该涵盖计算模型和计算复杂性。 计算模型应该涵盖有限状态自动机、正则语言(和正则表达式)、下推自动机、上下文无关语言、形式文法、图灵机、Lambda演算和不可判定性。 在本科水平上,学生应该至少学习足够的复杂性,以理解P、NP、NP-Hard和NP-Complete之间的区别。

推荐阅读

  • 《计算理论导论》(Sipser著)
  • 《计算复杂性》(Papadimitriou著)
  • 《算法》(Sedgewick和Wayne著)
  • 《算法导论》(Cormen, Leiserson, Rivest和Stein著)

架构

没有比深入理解计算机架构更好的替代品。计算机科学家应该从晶体管开始理解计算机。

具体建议

对缓存、总线和硬件内存管理的良好理解是实现现代系统良好性能的关键。为了更好地掌握机器架构,学生应该设计和模拟一个小型CPU。

推荐阅读

  • 《nand2tetris》,从零开始构建计算机。
  • 《计算机组成与设计》(Patterson和Hennessy著)
  • Drepper的《每个程序员都应该知道的内存知识》

操作系统

任何足够大的程序最终都会变成操作系统。因此,计算机科学家应该了解内核如何处理系统调用、分页、调度、上下文切换、文件系统和内部资源管理。

具体建议

学生在真实操作系统上动手实践是很重要的。有了Linux和虚拟化,这比以往任何时候都容易。 为了更好地理解内核,学生可以:

  • 在启动过程中打印"hello world";
  • 设计他们自己的调度器;
  • 修改页面处理策略;
  • 创建他们自己的文件系统。

推荐阅读

  • 《Linux内核开发》(Love著)

网络

鉴于网络无处不在,计算机科学家应该对网络栈和网络内部的路由协议有深入的理解。在不可靠的传输协议(如IP)之上构建高效、可靠的传输协议(如TCP)的机制,对计算机科学家来说不应该是神秘的,而应该是核心知识。

计算机科学家必须理解协议设计中涉及的权衡——例如,何时选择TCP,何时选择UDP。(如果程序员在大规模使用UDP,他们还需要理解拥塞的社会影响。)

具体建议

考虑到现代程序员经常遇到网络编程,了解以下现有标准的协议是有帮助的:

  • 802.3 和 802.11
  • IPv4 和 IPv6
  • DNS、SMTP 和 HTTP

计算机科学家应该理解数据包冲突解决中的指数退避以及拥塞控制中的增增减减机制。每个计算机科学家都应该实现以下内容:

  • HTTP客户端和守护进程
  • DNS解析器和服务器
  • 命令行SMTP邮件发送器

没有学生在通过初级网络课程时,不应该没有用Wireshark抓取过他们导师的Google查询。

虽然要求所有学生从零开始在IP之上实现一个可靠的传输协议可能有些过分,但我可以说,这对我来说是一次极具变革性的学习经历。

推荐阅读

  • 《Unix网络编程》Stevens, Fenner 和 Rudoff 著

安全

安全的一个悲哀的事实是,大多数安全漏洞来自于粗心的编程。更悲哀的事实是,许多学校在培训程序员保护他们的代码方面做得很差。

计算机科学家必须意识到程序可能被破坏的手段。他们需要培养一种防御性编程的意识——思考自己的代码可能如何被攻击。

安全是最好在整个课程体系中传授的那种培训:每个学科都应该提醒学生其固有的漏洞。

具体建议

至少,每个计算机科学家需要理解:

  • 社交工程
  • 缓冲区溢出
  • 整数溢出
  • 代码注入漏洞
  • 竞态条件
  • 权限混淆

一些读者指出,计算机科学家还需要了解基本的IT安全措施,比如如何选择真正安全的密码,以及如何使用iptables正确配置防火墙。

推荐阅读

  • 《Metasploit:渗透测试指南》Kennedy, O'Gorman, Kearns 和 Aharoni 著
  • 《安全工程》Anderson 著

密码学

密码学使得我们的数字生活成为可能。计算机科学家应该理解并能够实现以下概念,以及在实现过程中常见的陷阱:

  • 对称密钥密码体系;
  • 公钥密码体系;
  • 安全哈希函数;
  • 挑战-响应认证;
  • 数字签名算法;
  • 阈值密码体系。

由于在密码体系实现中获取足够随机数是一个常见错误,每个计算机科学家都应该知道如何为手头的任务获取足够随机的数。至少,几乎所有的数据泄露事件都表明,计算机科学家需要知道如何为存储加盐和散列密码。

具体建议

每个计算机科学家都应该体验一下使用手工制作的统计工具破解前现代密码体系下的密文的乐趣。 RSA足够容易实现,每个人都应该尝试。 每个学生都应该创建自己的数字证书,并在apache中设置https(这出奇地困难)。 学生还应该编写一个通过SSL连接的控制台Web客户端。

作为绝对实用的问题,计算机科学家应该知道如何使用GPG;如何使用ssh的公钥认证;以及如何加密一个目录或硬盘。

推荐阅读

  • 《Cryptography Engineering》Ferguson, Schneier 和 Kohno 著。

软件测试

软件测试必须贯穿整个课程体系。

具体建议

软件工程课程可以涵盖测试的基本风格,但没有什么能代替实践这门艺术。 学生的测试用例应该作为评分的依据。

用户体验设计

程序员经常为其他程序员编写软件,或者更糟糕的,为自己编写。

用户界面设计(或更广泛地说,用户体验设计)可能是计算机科学中最被低估的方面。

即使在教授中也存在一个误区,即用户体验是一种“软”技能,无法教授。 实际上,现代用户体验设计是基于人因工程和工业设计的实证原则。

如果没有其他,计算机科学家应该知道,界面需要使执行任何任务的难易程度与任务的频率乘以其重要性成比例。

推荐阅读

  • Paul Graham 关于 Web 2.0 的文章。
  • Spolsky 的《The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets》。
  • Duckett 的《HTML and CSS: Design and Build Websites》。
  • Flanagan 的《JavaScript: The Definitive Guide》。

数据可视化

良好的数据可视化是将数据以一种方式呈现,使人类将其感知为信息。这不是一件容易的事情。

现代世界是数据的海洋,利用人类感知的局部最大值是理解它的关键。

推荐阅读

  • Tufte 的《The Visual Display of Quantitative Information》。

并行性

并行性又回来了,而且比以往任何时候都更复杂。 不幸的真相是,利用并行性需要对架构有深入的了解:多核、缓存、总线、GPU 等。

并且,需要大量的实践。

具体建议

目前并不清楚并行编程的“最终”答案是什么,但一些特定领域的解决方案已经出现。 目前,学生应该学习 CUDA 和 OpenCL。 线程是并行性的一个脆弱的抽象,特别是当涉及到缓存和缓存一致性时。但是,线程很受欢迎,也很棘手,值得学习。Pthreads 是一个相当便携的线程库,值得学习。 对于任何对大规模并行性感兴趣的人,MPI 是一个先决条件。 在原理方面,map-reduce 似乎是持久的。

软件工程

软件工程的原则变化的速度几乎和编程语言一样快。一个好的、实践性的团队软件构建课程,可以提供对这项工作固有陷阱的工作知识。 几位读者建议,学生应该分成三人一组,通过三个不同的项目轮流担任领导角色。 学会如何在一个大型现有代码库中进行攻击和操纵,是大多数程序员必须掌握的技能,这也是最好在学校而不是在工作中学习的技能。

具体建议

所有学生都需要理解集中式版本控制系统如svn,以及分布式版本控制系统如git。 对像gdb和valgrind这样的调试工具有工作知识,在最终变得必要时会大有帮助。

推荐阅读

  • 《Version Control by Example》Sink著

形式方法

随着对安全、可靠软件的需求增加,形式方法有一天可能最终成为实现它的唯一手段。 目前,软件的形式建模和验证仍然是具有挑战性的,但该领域的进展是稳定的:每年都变得更容易。 今天计算机专业的学生在他们的一生中,甚至可能会有一天形式化软件构建成为一个预期的技能。

每个计算机科学家至少应该对使用一个定理证明器感到适度的舒适。(我认为用哪一个并不重要。)

学习使用定理证明器会立即影响编码风格。 例如,人们本能地对没有涵盖所有可能性的匹配或开关语句感到过敏。 而且,在编写递归函数时,定理证明器的用户有强烈的消除非法基础的冲动。

推荐阅读

  • 《Software Foundations》

计算机图形学与仿真

没有哪个学科比图形学更受“聪明”支配。

这个领域被推向,甚至由“足够好”定义。 因此,没有什么比图形学和仿真更能教授聪明的编程或对优化努力的扎实欣赏了。 我学到的超过一半的编码技巧来自于我对图形学的研究。

具体建议

可以在不到100行代码中构建简单的光线追踪器。 弄清楚在线框3D引擎中执行透视3D投影所需的变换是良好的思维卫生。

像BSP树这样的数据结构和像z-buffer渲染这样的算法是聪明设计的绝佳例子。

在图形学和仿真中,还有很多其他例子。

推荐阅读

  • 《Mathematics for 3D Game Programming and Computer Graphics》Lengyel著

机器人学

机器人学可能是教授初级编程最吸引人的方式之一。而且,随着机器人技术成本的持续下降,我们已经跨过了一些门槛,这将使得个人机器人革命成为可能。对于能够编程的人来说,个人物理自动化的难以想象的高度正变得触手可及。

相关文章:

  • 机器人的多点触控手势控制

人工智能

即使没有其他原因,仅仅因为人工智能对计算的早期历史有着巨大的影响,计算机科学家也应该研究人工智能。虽然智能机器的最初梦想似乎还很遥远,但人工智能已经催生了许多实际领域,如机器学习、数据挖掘和自然语言处理。

推荐阅读:

  • 《Artificial Intelligence》Russell 和 Norvig 著

机器学习

除了其出色的技术优势外,"相关性工程师"职位的大量空缺表明,每个计算机科学家都应该掌握机器学习的基础知识。机器学习特别强调了对概率和统计学的理解。

具体建议: 在本科阶段,核心概念应包括贝叶斯网络、聚类和决策树学习。

推荐阅读:

  • 《Machine Learning》Mitchell 著

数据库

数据库太常见、太有用了,不容忽视。理解驱动数据库引擎的基础数据结构和算法是有用的,因为程序员经常在更大的软件系统中重新实现数据库系统。关系代数和关系演算在次图灵模型计算中是杰出的成功案例。

与UML建模不同,ER建模似乎是对软件工件的设计和约束进行可视化编码的合理机制。

具体建议: 能够设置和操作LAMP堆栈的计算机科学家距离拥有自己的公司只有一步之遥,并且需要大量的辛勤工作。

推荐阅读:

  • 《SQL和关系理论》Date 著

通用阅读建议

  • 《Gödel, Escher, Bach》Hofstadter 著
  • Nick Black给硕士生的建议

还有什么? 我的建议受到我自身知识盲点的限制。我没有列出这里应该包括的内容是什么?

相关文章:

  • 如何获得终身教职
  • 将BibTeX解析为S-Expressions、JSON、XML和BibTeX
  • PAANDA:学术界的保密协议
  • 大学技巧、窍门和秘籍
  • 捍卫博士学位的技巧
  • 如何回应同行评审
  • 研究生的12个决议
  • 如何进行科学工作的同行评审
  • 电子肉
  • 同行堡垒:科学战场
  • 你问题的形状
  • 低成本学术博客的6个技巧
  • 博士学位的图解指南
  • CRAPL:学术界的开源许可证
  • 为什么同行评审者应该使用TOR
  • 学术工作搜索建议