【译】每一个计算机科学专业的学生应该知道的知识(完整版)

1,217 阅读23分钟
原文链接: data.hiqiuyi.cn

原文地址:matt.might.net/articles/wh…

摘要:由于计算机科学领的飞速发展,要想辨识出现代计算机科学学位包含哪些知识,是一件具有挑战性的事情。

“什么是每一个计算机专业的学生应该知道的知识?”,教职员工们正在参与这场讨论,我汇总了我的想法作为这个问题的答案。

我试着把这个问题作为下面四个问题的结合来回答:

  • 每个学生应该知道什么才能获得一份好的工作
  • 每个学生应该知道什么才能维持终身就业
  • 每个学生应该知道什么才能进入研究生院
  • 每个学生应该知道什么才能对社会有利

我下面的想法包含与现代计算机相关的一般原则具体建议
计算机专业的学生,请自由使用这篇文章作为自学指南。

如果有任何添加和删除的建议,请发电子邮件或推特给我。
邮箱:matt-blog@might.net
推特:www.twitter.com/mattmight

更新:谢谢您的建议和提醒,当我收到后,我会将其整理进文档,以保持积极更新。

作品集 VS 简历

计算机科学项目从工程和数学中脱离而出,采取了一种基于简历的方法来招聘毕业生。

一份简历无法说明程序员的能力。

每一个计算机专业的学生应该建立一个作品集。

作品集应该和个人博客一样简单,上面有每一个项目或成就的文章。一个更好些的作品集应该包含每个项目的文章以及可公开浏览的代码(托管在github或者Google code上)。

应该链接并记录对开源软件的贡献。

代码作品集能够让雇主直接判断你的能力。

GPAs(平均分)和简历却做不到。

教授们应该设计课程项目来丰富作品集,学生在每一个课程完结时,应该花时间去更新作品集。

作品集例子:

技术交流

计算机科学中的孤狼是一种濒临灭绝的物种。

现代计算机科学家必须习惯于和非程序员有说服力地、清晰地交流。

在较小的公司,程序员是否能和管理人员交流想法,可能会关系到公司的成败。

不幸的是,通过增加一门课程无法解决这个问题。(尽管技术交流方面的扎实课程并不会有任何坏的影响)

需要更多的课程提供学生机会,去展示他们的工作,并通过口头表达来保护他们的想法。

具体建议

建议学生掌握一个像PPT和(我最喜欢)Keynote一样的展示工具。(抱歉,虽然我很喜欢基于LaTeX的展示工具,但这些工具太过静态)

对于制作美观的数学文档,LaTex是最好的选择。

所有技术文档的书面作业都应该用LaTeX提交。

推荐阅读

工程核心

计算机科学不完全等同于工程学。
但是,足够接近。

计算机科学家会发现自己和工程师一起工作。

计算机科学家和传统的工程师需要说同样的语言—一门来源于实时分析,线性代数,概率论和物理的语言。

计算机科学家应该通过电磁场来研究物理学。但是,要做到这一点,它们需要通过多元微积分(以及用于良好度量的微分方程)来处理。

在进行声音信号仿真时,掌握概率和线性代数(通常是线性代数)是无比重要的。在解释结果时,没有任何东西可以代替对统计学的扎实理解。

推荐阅读

Unix哲学

计算机科学家应该适应并实践Unix计算哲学。
Unix哲学(相对于Unix本身)强调语言抽象和组成来实现计算。

在实践中,这意味着对命令行计算,文本文件配置,无IDE软件开发的概念感到满意。

具体建议

由于Unix systems的流行,今天的计算机科学家应该能流利地掌握基本的Unix,包括如下能力:

  • 浏览和操作文件系统;
  • 用管道进行组合操作;
  • 习惯于用emacsvim编辑文件;
  • 给一个软件项目创建、修改和执行Makefile;
  • 写一个简单的shell脚本。

学生们在不了解Unix哲学的强大能力前会抵制它。因此,最好的方法是激发学生去完成在Unix上具有相对优势的有用任务。
比如:

  • 从给定的目录中找出占磁盘空间最大的5个文件夹。
  • 从计算机上找出重复的MP3文件(从内容判断,而不是文件名)
  • 取一列姓名,其名称和姓氏都是小写,对他们进行合理的调整。
  • 找出第二个字母是x,倒数第二个字母是n的所有英文单词。
  • 一台电脑的麦克风输入直接通过网络从另一台电脑的扩音器输出。
  • 给定一个目录,将所有文件的文件名中的空格替换成下划线。
  • 报告web服务器中来自特定IP地址的最后十次错误访问。

推荐阅读

系统管理

一些计算机科学家嘲笑系统管理是一项“IT”任务。
他们认为一个计算机科学家可以教自己做任何技术人员能做的事情。
这是真的。(理论上)

然而,这种态度是错误的:计算机科学家必须能够胜任和安全地管理自己的系统和网络。

软件开发中的许多任务都是在不经过系统管理员的情况下最有效地执行的。

具体建议

每一个现代计算机科学家都应该会:

  • 安装和管理Linux发行版。
  • 配置和编译Linux内核。
  • 用dig,ping和traceroute定位连接。
  • 编译和配置类似apache一样的web服务器。
  • 编译和配置类似bind一样的DNS守护进程。
  • 使用文本编辑器维护网站。
  • 切断和卷曲网线。

推荐阅读

编程语言

编程语言随着一定周期上升和下降。

程序员的生涯不应该如此。

虽然教授与找工作有关的语言很重要,但同样重要的是,学生要学会如何自学新语言。

学会自学编程语言的最好方法是,学习多种语言和多种编程范式。

学习第n种语言的难度,是学习上一种语言难度的一半。

然而,要真正理解编程语言,就必须实现一种语言。理想情况下,每一个计算机科学专业的学生应该上一门编译原理的课程。至少,应该实现一个编译器。

下列语言提供了范式和实际应用的合理搭配:

具体建议

  • Racket;
  • C;
  • JavaScript;
  • Squeak;
  • Java;
  • Standard ML;
  • Prolog;
  • Scala;
  • Haskell;
  • C++; and
  • Assembly.

Racket

Racket,作为拥有Lisp全部特征的方言,有着非常简单的语法。

对一小部分学生来说,这种语法是一个障碍。

坦率的说,如果这些学生甚至对暂时地接收一种不同的语法规则有着基本的心理障碍的话,那么在他们的计算机科学生涯中会缺少灵敏。

Racket强大的宏系统和高阶编程功能彻底磨平了数据和代码之间的鸿沟。

如果教得正确,Lisp将得到解放。

推荐阅读

How to Design Programs by Felleisen, Findler, Flatt and Krishnamurthi.
The Racket Docs.

ANSI C—美国国家标准协会(ANSI)对C语言发布的标准

C 是对硅的简洁至极的抽象。

在嵌入式系统里编程,C独孤求败。

学习C能够深入理解占主导地位的冯诺依曼结构,这是其它语言无法做到的。

由于糟糕的C编程在缓冲区溢出安全攻击的流行中扮演了密切的角色,所以程序员学习正确的C编程非常重要。

推荐阅读
  • ANSI C by Kernighan and Ritchie.

JavaScript

JavaScript很好地代表了动态、高级语言(如Python,Ruby,Perl)的语义模型。

作为web的原生语言,它的实用优势是独一无二的。

推荐阅读

JavaScript: The Definitive Guide by Flanagan.
JavaScript: The Good Parts by Crockford.
Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript by Herman.

Squeak

Squeak是Smalltalk的现代方言,最纯粹的面向对象语言。

它赋予了“面向对象”的本质。

推荐阅读

Java

Java将流行很久以至于不能忽略它。

推荐阅读

Effective Java by Bloch.

Standard ML

Standard ML是Hindley-Milner系统的清晰体现。

Hindley-Milner是现代计算领域最伟大(但却是最不知名)的成就之一。

虽然Hindley-Milner的类型推到是指数级的复杂度,但是对人们感兴趣的程序总是很快。

该类型系统足够丰富,可以表达复杂的结构不变量。它是如此丰富,事实上,类型良好的程序通常是没有错误的。

推荐阅读

ML for the Working Programmer by Paulson.
The Definition of Standard ML by Milner, Harper, MacQueen and Tofte.

Prolog

尽管逻辑编程在应用领域中占有一席之地,但它是计算思维的另一种范式。

对于程序员可能需要在另一个范例中模仿它的情况,理解逻辑编程是值得的。

另外一种值得学习的逻辑语言是miniKanren.miniKanren强调存粹的逻辑编程(不允许切割)。这种约束已经演变成另外一种逻辑编程风格,称为关系程序设计,并且它授予Prolog程序通常不支持的属性。

推荐阅读

Learn Prolog Now!
Another tutorial.
miniKanren.

Scala

Scala是一门将函数式编程和面向对象编程融合很好的语言。Scala是Java应该成为的样子。

Scala建立在Java虚拟机之上,兼容现有的Java代码库,因此,它最有可能成为Java的后继者。

推荐阅读

Haskell

Haskell是Hindley-Milner语言家族皇冠上的宝石。

Haskell充分利用了惰性,在任何主要编程语言中,Haskell最接近于纯数学编程。

推荐阅读

ISO C++

C++是必不可少的邪恶的力量。

但是既然必须教它,就得教完整。

特别是,计算机专业学生应该掌握模板元编程

推荐阅读

The C++ Programming Language by Stroustrup.
C++ Templates: The Complete Guide by Vandevoorde and Josuttis.
Programming Pearls by Bentley.

Assembly

任何汇编语言都可以。

因为X86很流行,所有它可能最合适。

学习编译器是学习汇编的最好方法,因为它让计算机科学家对高级代码的转换有了直观的感觉。

具体建议

计算机科学家应该理解
产生式编程(宏);
语法(动态)范围;
闭包;
原型;
高阶函数;
动态分派;
子类型;
模块;
函子;
与任何特定语法不同的语义概念。

推荐阅读

Structure and Interpretation of Computer Programs by Abelson, Sussman and Sussman.
Lisp in Small Pieces by Queinnec.

离散数学

计算机科学家必须对形式逻辑和证明有扎实的掌握。

通过代数运算和自然推理的证明,应用于推理和日常编程任务。

归纳证明用于递归函数构造中的推理。

计算机科学家必须熟悉正式的数学符号和对基本的离散结构的严格推理。这些离散结构包括:sets, tuples, sequences, functions和power sets。

具体建议

对于计算机科学家来说,研究下面结构的推理是很重要的。

  • 树;
  • 图;
  • 形式语言;
  • 自动机

学生们应该学习足够的数字理论来研究和实现常见的密码协议。

推荐阅读

数据结构与算法

学生们应该去看常见的(或者不常见但异常有效)的数据结构和算法。

对计算机科学家而言,比了解一种特定的算法或数据结构(通常很容易找到),更重要的是知道怎么去设计算法(比如贪心算法,动态规划策略)以及怎样跨越算法的想法和实现的细节之间的鸿沟。

具体建议

至少,想要长期稳定就业的计算机科学家应该了解以数据结构或算法:

  • 哈希表;
  • 链表;
  • 树;
  • 二叉查找树;和
  • 有向和无向图。

计算机科学家应该准备好实现或扩展的算法,去操作这些数据结构包括搜索元素、添加元素和删除元素。

为了完整,计算机科学家应该知道每一种算法的命令式和函数式版本。

推荐阅读

CLRS.
Any of the Art of Computer Programming series by Knuth.

理论

掌握理论是在研究生院研究的前提条件。

理论对找出问题的硬边界(或者找出一种方法避开最初的硬边界)是至关重要的。

计算复杂性可以合法地声称是所有计算机“科学”中为数不多的真正的预测理论之一。

计算机科学家必须知道可处理性和可计算性的界限在哪里。

忽略这些限制,轻则招致挫折,重则招致失败。

具体建议

在本科生阶段,理论致少应该包括计算模型和计算复杂度。

计算模型应该包含有限状态机,正则语言(和正则表达式),下推自动机,上下文无关语言,形式语法,图灵机,λ演算和不可判定性。

在本科阶段,学生至少应该足够理解P,NP,NP-Hard和NP-Complete之间的差异。

避免留下错误的印象,学生们应该通过将一些大的NP问题规约成SAT问题,并用SAT模型解决。

推荐阅读

Introduction to the Theory of Computation by Sipser.
Computational Complexity by Papadimitriou.
Algorithms by Sedgewick and Wayne.
Introduction to Algorithms by Cormen, Leiserson, Rivest and Stein.

计算机体系结构

对计算机体系结构的理解无比重要。

计算机科学家应该从晶体管上理解计算机。

对计算机体系结构的理解应该包括标准的抽象:晶体管、门、加法器、多路复用器、触发器、算术和逻辑单元、控制单元、缓存和随机存取存储器。

在可见的未来,理解高性能计算的GPU模型非常重要。

具体建议

正确理解缓存、总线和硬件内存管理对于在现代系统上获得良好的性能至关重要。

为了良好的掌握计算机体系结构,学生们应该设计和模仿一个CPU。

推荐阅读

nand2tetris, which constructs a computer from the ground up.
Computer Organization and Design by Patterson and Hennessy.
“What every programmer should know about memory” by Drepper.

操作系统

任何一个足够大的程序,最后都变成了操作系统。

因此,计算机科学家应该知道内核如何处理系统调用、分页、调度、上下文切换、文件系统和内部资源管理。

要获得好的性能,对操作系统的理解仅次于对编译器和计算机体系结构的理解。

理解操作系统(我的意思是包含运行时系统)对在没有操作系统的嵌入式系统上编程特别重要。

具体建议

对学生们来说,在真正的操作系统上动手实践非常重要。

有Linux和虚拟机的帮助,这比以前容易多了。

给了更好的理解内核,学生们应该:

  • 在引导过程中打印“hello world”;
  • 设计自己的调度程序;
  • 修改页面处理策略;
  • 创建自己的文件系统。

推荐阅读

Linux Kernel Development by Love.

计算机网络

由于网络无处不在,计算机科学家应该对网络中的网络堆栈和路由协议有一个明确的理解。

在非可靠的传输协议(如IP)上建立可靠的传输协议(如TCP)的技术,对计算机科学家不应该是魔法。这应该是核心知识。

计算机科学家必须理解协议设计所涉及的权衡—例如,何时选择TCP,何时选择UDP。(程序员应该理解UDP能更大规模的传输)

具体建议

由于现代程序员越来越多的要进行网络编程,了解现有标准的协议是有帮助的,例如:

  • 802.3 and 802.11;
  • IPv4 and IPv6; and
  • DNS, SMTP and HTTP.

计算机科学家应该了解数据包冲突解决中的指数后退和拥塞控制中涉及的加性增加乘性减少机制。

每一个计算机科学家应该实现下面的:

  • 一个HTTP客户端和守护进程;
  • 一个DNS解析器和服务器;
  • 一个命令行的SMTP邮箱。

如果没有通过wireshark嗅探过导师的Google查询,那么学生就不应该通过计算机网络入门课程。

要求所有的学生在IP协议的基础上,从零实现一个可靠的传输协议,可能有些过分。但是我敢说,完成这些对学生时代的我,是变革性的体验。

推荐阅读

Unix Network Programming by Stevens, Fenner and Rudoff.

安全

对于安全问题,可悲的是,绝大多数安全漏洞来自于草率的编程。更可悲的是,很多大学花费很少的时间在训练程序员编写安全的代码。

计算机科学家必须知道程序被破坏的方法。

计算机科学家需要形成一种防御编程的感觉—思考他们自己的代码可能被怎么样攻击。

安全性是在整个课程中最好分配的训练:每个学科都应该提醒学生注意其固有的脆弱性。

具体建议

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

  • 社会工程学
  • 缓冲区溢出
  • 整数溢出
  • 代码入侵漏洞
  • 竞态条件
  • 权限混乱

一些读者指出,计算机科学家需要知道基本安全方法,如:如何选择合理的好的密码。如何通过iptables合理地配置防火墙。

推荐阅读

密码学

密码学让我们的数字生活编程可能。

计算机科学家应该理解和实现下面的概念,以及在实现的过程中遇到的常见陷阱:

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

由于这是密码系统实现中的常见错误,因此每个计算机科学家都应该知道如何为手头的任务获取足够的随机数。

至少,正如几乎每一次数据泄露所显示的那样,计算机科学家需要知道如何为存储进行加密和散列密码。

每一个计算机科学家都应该感兴趣于用手工统计工具破解前现代密码系统的密码。

RSA很容易实现,每个人都应该实现它。

每一个学生应该在apache上创建自己的数字证书和建立https。(做到这点是非常难的)

学生还应该编写一个控制台Web客户端,通过SSL连接。

作为严格的实践事项,计算机科学家应该知道如何使用GPG; 如何使用公钥认证来进行SSH; 以及如何加密目录或硬盘。

推荐阅读

软件测试

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

软件工程课程可以涵盖测试的基本风格,但没有任何方法可以替代这种艺术。

老师应该对学生所交的测试用例进行评分。

我用学生提交的测试用例,测试所有学生的作业。

学生们似乎并不关心开发防御性测试用例。但当写测试用例用于测试同学的程序时,则不遗余力。

用户体验设计

程序员经常给其他程序员写程序,至少,给自己写。

用户界面设计(或者更广泛地说,用户体验设计)可能是计算机科学中最不受重视的方面。

有一个错误的观念,甚至有些教师也会错误的认为,用户体验是一门软技能,无法教授。

实际上,现代用户体验设计是以人体工程和工业设计为原则的。

至少,计算机科学需要知道接口应该被设计成,执行任务的容易度与任务的频率乘以任务的重要性成正比。

实际上,每一个程序员应该熟悉用HTML,CSS,JavaScript设计可用的web接口。

推荐阅读

可视化

好的可视化渲染数据让人们洞察信息。这不是一件容易的事情。

现代世界是一个数据的海洋,利用人们能够感知的局部极大值是感知现代世界的钥匙。

推荐阅读

并行

并行回来啦,而且比以前更丑。

不幸的是,掌握好并行需要深入理解体系结构:多核、高速缓存、总线、GPU等等。

以及实践,很多实践。

具体建议

并行编程的最终答案还不清晰,但是一些特定领域的解决方案已经出现。

现在,学生们应该学习CUDA和OpenCL.

线程是并行性的脆弱抽象,特别是当涉及缓存和缓存一致性时。但是,线程很受欢迎,而且很棘手,因此值得学习。Pthreads是一个可移植的线程库,值得学习。

对于任何对大规模并行感兴趣的人来说,MPI是一个先决条件。

从原则上看,map-reduce似乎是持久的。

软件工程

软件工程的原则同编程语言的变化一样的快。

团队软件建设实践中的一门良好的、动手的课程,提供了关于团队软件建设中固有的陷阱的工作知识。

几个读者推荐,学生分为三人一组,在三个不同的项目中轮流当组长。

学习如何利用现有的大型代码库是大多数程序员必须掌握的技能,这是最好在学校学到而不是在工作中学到的技能。

具体建议

所有学生都需要了解集中式版本控制系统(如SVN)和分布式版本控制系统(如GIT)

当你使用调试工具(如gdb和valgrind)时,它们的实用知识将起很大作用。

推荐阅读

形式化方法

随着对安全可靠的软件需求的增加,正式的方法最终可能成为交付软件的唯一手段。

目前,软件的形式化建模和验证仍然具有挑战性,但该领域的进展是稳定的:它每年都会变得更容易。

在今天的计算机科学专业学生的有生之年,这一天会到来,正规的软件构建是一种被要求的技能。

每一个计算机科学家至少应该慢慢地适应用理论证明器。(哪一种并不重要)

学会使用代码证明器快速影响代码风格。

比如,一个人本能地讨厌写未覆盖所有可能的matchswitch语句。

而且,在编写递归函数时,定理证明器的用户有强烈的欲望去消除不良的根源。

推荐阅读

图形和仿真

没有比图形学更“聪明”的学科了。

这个领域被“足够好”驱动和定义。

因此,图形和仿真是教授聪明编程和欣赏优化工作的最好方法。

我学到的编码有一半以上来自于我对图形的研究。

具体建议

简单的射线追踪器可以在100行代码下构建。

计算在一个线框3D引擎中运行透明的3D投影所需要的转换是一件很颇费脑力的事情。

像BSP树的数据结构和像z-buffer的算法是体现聪明设计非常好的例子。

在图形和仿真领域,这样的例子还有很多。

推荐阅读

机器人技术

机器人技术可能是教授入门编程最吸引人的方法之一。

而且,随着机器人技术成本的持续下降,限制个人机器人技术的门槛将被跨越。

对于那些能够编程的人来说,个人物理机难以想象的自动化程度时代即将到来。

相关文章

人工智能

仅仅是考虑到人工智能对计算机早期历史的影响,计算机科学家应该研究人工智能。

虽然离人工智能当初的梦想还很远,但人工智能激发了很多实用的领域。如:机器学习,数据挖掘和自然语言处理。

推荐阅读

机器学习

除了其出色的技术优点之外,“相关工程师”的大量空缺职位表明,每一位计算机科学家都应该掌握机器学习的基本原理。

机器学习重点强调了理解概率和统计的必要性。

具体建议

在本科生阶段,应该掌握核心概念:贝叶斯网络,聚类,和决策树学习。

推荐阅读

数据库

数据库十分常见和有用,以至于不能被忽略。

对驱动数据库引擎的基本数据结构和算法的理解非常有用。因为在一个大的软件系统里,程序员经常重构数据库系统。

关系代数和关系演算在计算的子图灵模型中脱颖而出.

与UML模型不同,ER模型对可视化编程的设计以及软件构建的约束似乎更合理。

具体建议

建立和操作LAMP栈对计算机科学家来说是一个好主意。在运行他们自己的公司的时候,很多艰难的工作将不在存在。

推荐阅读

非特定推荐阅读

其它

我的建议受限于我的知识盲点。

你觉得还有哪些知识没有被列出来呢?