[译]每一个计算机科学专业都应该知道的

原文链接:http://matt.might.net/articles/what-cs-majors-should-know/

鉴于该领域的广泛增长,罗列出现代计算机科学学位需要学习的内容变得富有挑战性。

我的老师们正在参与这场讨论,所以我把我的想法结合起来作为对“每个计算机科学专业的人应该知道什么?”这个问题的回答。

我试图用以下四个子问题来回答这个问题:

  1. 学生怎样才能找到一份好工作?
  2. 学生怎样才能维持终身就业?
  3. 学生怎样才能考上研究生院?
  4. 学生怎样才能造福社会?

我下面的想法考虑了与现代计算领域相关的一般性原则和具体建议。
计算机科学专业的同学们:请随意使用它作为自学指南。

请发送电子邮件或推特,提出添加和删除建议。

Update:感谢您的建议和意见!当我收到它们时,我会把合并起来,保持这个文件的更新。

作品相对与简历

以工程和数学为专业重点,但计算机科学专业却采用基于简历的方法来雇佣毕业生。
一个简历说明不了程序员的能力。
每一个计算机科学专业的学生应建立一个作品集。
作品集可以像个人博客一样简单,每个项目或成就都有一个帖子。更好的方式是包括每个项目页面和可公开浏览的代码(可能在github或Google代码上托管)。
还可以有说明和链接指向对开源的贡献。
代码作品可以让雇主直接判断作者的能力,但GPA和简历不行。
教授应该设计以便于学生开发个人作品的课程,学生在每门课程结束后花时间更新它们。

例子

  1. 初开的网站<( ̄︶ ̄)>
  2. Edward Yang's web site.
  3. Michael Bradshaw's web site.
  4. Github is my resume.

技术交流

计算机科学中的孤狼是会濒临灭绝的。
现代计算机科学家必须有能力地将他们的想法传达给非程序员。
在小公司中,程序员是否可以将自己的想法传达给管理层,可能会对公司的成败产生影响。
不幸的是,这并不是通过单个课程可以解决的问题(尽管技术交流中可靠的课程不会产生负面影响)。
我们需要更多课程为学生提供展示作品和自己想法的机会。

具体建议

我建议学生掌握PPT或(我最喜欢的)Keynote等演示工具。(对不起,我喜欢它们,基于LaTeX的演示工具太过静态了。)
LaTeX可以制作漂亮的数学文档,,技术课程中的所有书面作业都应该用LaTeX中提交。

推荐阅读

  1. Writing for Computer Science by Zobel.
  2. Even a Geek Can Speak by Asher.
  3. The LaTeX Companion.
  4. The TeXbook by Knuth.(Warning: Experts only.)
  5. Notes on Mathematical Writing.
  6. Simon Peyton-Jones's advice on How to Give a Good Research Talk.
  7. My advice on how to send and reply to email.

工程的重点

计算机科学不是很工程。
但是,它足够接近。
计算机科学家会发现自己与工程师合作。
计算机科学家和传统工程师需要讲同一种语言 - 一种植根于实际分析,线性代数,概率和物理学的语言。
计算机科学家应该通过电磁学来学习物理学。但是,要做到这一点,他们需要通过多元微积分(和微分方程式)进行测量。
在构建声音模拟时,概率和(通常是)线性代数的命令是非常宝贵的。在解释结果时,无法取代对统计数据的充分理解。

推荐阅读

  1. Calculus(微积分)by Spivak.。
  2. All of Statistics: A Concise Course in Statistical Inference by Wasserman.

Unix哲学

计算机科学家应该熟悉并实践Unix的计算哲学。
Unix哲学(与Unix本身相反)是强调语言抽象和组合以实现计算的哲学。
实际上,这意味着需要理解命令行计算,文本文件配置和无IDE软件开发的概念。

具体建议

鉴于Unix系统的普及,今天的计算机科学家应该能够熟练掌握基本的Unix,包括:

  1. 切换和操纵文件系统;
  2. 管道机制;
  3. 用emacs和vim编辑文件;
  4. 为软件项目创建,修改和执行Makefile文件;
  5. 编写简单的shell脚本。

学生往往会拒绝学习Unix哲学,除非他们了解它的用处。因此,最好让学生完成发挥Unix优势的任务,例如:

  1. 找到给定目录中占用最多空间的五个文件夹。
  2. 打印计算机上重复的MP3(按文件内容,而不是文件名)。
  3. 列出姓名列表,并对其进行适当的组合。
  4. 找到所有第二个字母为x和倒数第二个字母为n的单词。
  5. 通过网络直接将麦克风输入路由到另一台计算机的扬声器。
  6. 将给定目录下的文件名中的所有空格替换为下划线。
  7. 打印来自特定IP地址的Web服务器的最近十次错误访问。

推荐阅读

  1. The Unix Programming Environment by Kernighan and Pike.
  2. The Linux Programming Interface: A Linux and UNIX System Programming Handbook by Kerrisk.
  3. Unix Power Tools by Powers, Peek, O'Reilly and Loukides.
  4. commandlinefu.
  5. Linux Server Hacks.
  6. The single Unix specification.

系统管理

一些计算机科学家嘲笑系统管理是一项“IT”任务。
我们的想法是,计算机科学家可以自学做技术人员可以做的任何事情。
这是真的。(理论上。)
然而,这种态度是错误的:计算机科学家必须能够胜任并安全地管理他们自己的系统和网络。
软件开发中的许多任务不需要系统管理员就可以最有效地执行。

具体建议

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

  1. 安装和管理Linux发行版。
  2. 配置并编译Linux内核。
  3. 通过dig、ping和traceroute排查连接。
  4. 编译和配置像apache这样的Web服务器。
  5. 编译并配置像bind这样的DNS守护进程。
  6. 使用文本编辑器维护网站。
  7. 切断和压接网线。

推荐阅读

UNIX and Linux System Administration Handbook by Nemeth, Synder, Hein and Whaley.

编程语言

编程语言的热度会周期性的升降。
程序员的职业生涯不应该这样。
虽然教授与雇主相关的语言很重要,但学生学习如何自学新语言同样重要。
学习如何学习编程语言的最佳方法是学习多种编程语言和编程范例。
学习第n语言的难度是第(n-1)难度的一半。
然而,要真正理解编程语言,就必须自己实现一个。理想情况下,每个计算机科学专业的同学都会参加编译器课程。每个计算机科学专业的同学至少应该实现一个解释器。

具体语言

以下语言提供了范例和实际应用的合理组合:

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

Racket

Racket作为Lisp的全功能方言,语法非常简单。
但对于一小部分学生来说,这种语法是一种障碍。
坦率地说,如果这些学生即使能暂时接受外来句法制度也存在根本的心理障碍,他们缺乏在计算机科学生涯中生存的精神灵巧。
Racket强大的宏系统和高阶编程设施彻底消除了数据和代码之间的界限。
如果正确教导,Lisp就可以解放了。

推荐阅读

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

ANSI C

C语言是对”硅“简单而无情的抽象。
C在编写嵌入式系统时仍然没有竞争对手。
学习C可以以一种其他语言无法做到的方式,来深刻理解冯·诺伊曼体系结构。
鉴于可怜的C在缓冲区溢出的安全漏洞中发挥着密切作用,因此程序员必须学习如何正确编码C。

推荐阅读

  1. ANSI C by Kernighan and Ritchie.

JavaScript

JavaScript是流行的动态高阶语言(如Python,Ruby和Perl)中语义模型良好的代表。
作为网络的母语,其实用优势是独一无二的。

推荐阅读

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的现代方言,是最纯粹的面向对象语言。
它赋予了“面向对象”的本质。

推荐阅读

  1. Introductions to Squeak

Java

Java将长期受欢迎,并且无法忽略它。

推荐阅读

  1. Effective Java by Bloch.

Standard ML

Standard ML是Hindley-Milner系统的干净实现。
Hindley-Milner型系统是现代计算领域最伟大(但鲜为人知)的成就之一。
尽管复杂性具有指数性,但Hindley-Milner中的类型推断非常快。
类型系统足够丰富,可以表达复杂的静态结构。事实上,它是如此丰富,以至于良好类型的程序通常没有错误。

推荐阅读

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

Prolog

虽然在应用程序中占有一席之地,但逻辑编程( logic programming)是计算思维的另一种范式。
对于程序员可能需要在另一个范例内模拟它的那些实例,值得理解逻辑编程。
另一种值得学习的逻辑语言是miniKanren。miniKanren强调纯粹(不允许切割)逻辑编程。这种约束已经发展出一种称为关系编程的另一种逻辑编程方式,它赋予了Prolog程序特殊的属性。

推荐阅读

  1. Learn Prolog Now!
  2. Another tutorial.
  3. miniKanren.

Scala

Scala是一个精心设计的函数式编程和面向对象编程语言的融合。Scala做了Java本来应该做的。
它建立在Java虚拟机之上,与现有的Java代码库兼容,因此,它最有可能成为Java的后继者。

推荐阅读

  1. Programming in Scala by Odersky, Spoon and Venners.
  2. Programming Scala by Wampler and Payne.

Haskell

Haskell是Hindley-Milner语系的皇冠上的明珠。
充分利用惰性求值,Haskell最接近主要编程语言的纯数学编程语言。

推荐阅读

  1. Learn You a Haskell by Lipovaca.
  2. Real World Haskell by O'Sullivan, Goerzen and Stewart.

ISO C ++

C ++是不可或缺的邪恶语言。
但是,既然必须教它,就必须全面教授。
特别是,计算机科学专业的学生应该掌握模板元编程。

推荐阅读

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

汇编

任何汇编语言都可以。
由于x86很受欢迎,它可能就是这样。
学习编译器是学习汇编的最佳方式,因为它使计算机科学家能够直观地了解如何转换高级代码。

具体建议

计算机科学家应该理解宏(macro); 词法(动态)作用域; 闭包; 延续(continuations); 高阶函数; 动态调度; 子类; 模块和算子; 特殊语义概念Monad。

推荐阅读

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

离散数学

计算机科学家必须牢牢掌握形式逻辑和证据证明。通过代数操作和自然演绎证明了常规编程任务的合理性。通过归纳证明了递归函数的合理性。
计算机科学家必须精通标准数学符号,并严格推理基本的离散结构:集合,元组,序列,函数和幂集。

具体建议

对于计算机科学家来说,重要的是要涵盖以下论证:

  1. 树;
  2. 图;
  3. 形式语言;
  4. 自动机。

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

推荐阅读

  1. How to Prove It: A Structured Approach by Velleman.
  2. How To Solve It by Polya.

数据结构和算法

学生当然应该看到常见的(或罕见但不合理的有效)数据结构和算法。

但是,比了解特定算法或数据结构(通常很容易查找)更重要的是,计算机科学家必须了解如何设计算法(例如,贪婪,动态规划)以及如何跨越算法之间的差距,理论及其实际的细节。

具体建议

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

  1. 哈希表;
  2. 链表;
  3. 树;
  4. 二叉树;
  5. 有向和无向图。

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

推荐阅读

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

理论

掌握理论是研究生的先决条件。
当理论为问题提供硬边界时(或者当它提供一种规避最初似乎是硬边界的手段时),理论是非常宝贵的。
计算复杂度是所有计算机“科学”中为数不多的真正的预测理论之一。
计算机科学家必须知道易处理性和可计算性的界限在哪里。忽略这些限制会在最好的情况下引起挫折,在最坏的情况下引起失败。

具体建议

在本科阶段,理论至少应涵盖计算模型和计算复杂度

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

在本科阶段,学生应该学习必要难度来理解P,NP,NP-Hard和NP-Complete之间的差异。
为了避免留下错误的印象,学生应该通过减少SAT和使用现代SAT求解器来解决NP中的一些大问题。

推荐阅读

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

架构

对计算机体系结构的深刻理解是无可替代的。
计算机科学家应该从晶体管中了解计算机。
对架构的理解应该包括标准的抽象级别:晶体管,门,加法器,多路复用器,触发器,ALU,控制单元,高速缓存和RAM。
在可预见的未来,对高性能计算的GPU模型的理解将是重要的。

具体建议

熟悉高速缓存,总线和硬件内存管理对于现代系统上的性能保障至关重要。
为了更好地掌握机器架构,学生应该设计并模拟一个小型CPU。

推荐阅读

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

操作系统

任何足够大的程序最终都会成为操作系统。
因此,计算机科学家应该了解内核如何处理系统调用,分页,调度,上下文切换,文件系统和内部资源管理。
对操作系统的充分理解以优化性能仅次于对编译器和体系结构的理解。
在编写嵌入式系统时,理解操作系统(我会自由地解释为包括运行时系统)变得尤为重要。

具体建议

对于学生来说,在真实的操作系统上弄脏手是很重要的。借助Linux和虚拟化,这比以前更容易。
为了更好地理解内核,学生可以:

  1. 在启动过程中打印“hello world”;
  2. 设计自己的调度程序;
  3. 修改页面处理策略;
  4. 创建自己的文件系统。

推荐阅读

  1. Linux Kernel Development by Love.

网络

鉴于网络无处不在,计算机科学家应该对网络中的网络堆栈和路由协议有一个深刻的理解。
在不可靠的传输协议(如IP)之上构建高效,可靠的传输协议(如TCP)的机制对计算机科学家来说不应该是神秘的,它应该是核心知识。
计算机科学家必须了解协议设计中权衡 - 例如,何时选择TCP以及何时选择UDP。(如果程序员在大规模使用UDP,他们需要了解对网络阻塞的影响。)

具体建议

鉴于现代程序员遇到网络编程的频率,了解现有标准的协议是有帮助的,例如:

  1. 802.3和802.11;
  2. IPv4和IPv6;
  3. DNS,SMTP和HTTP。

计算机科学家应该了解数据包冲突解决方案中的指数退避,以及阻塞控制中涉及的加法增加乘法减少的机制。

每个计算机科学家都应该实现以下功能:

  1. HTTP客户端和守护进程;
  2. DNS解析器和服务器;
  3. 命令行SMTP邮件程序。

任何学生在通过一个网络方面课程前,必须用wireshark嗅探过Google查询,
要求所有学生在IP协议上完成可靠的传输协议可能太过分了,但我可以说这对我来说是一次个人变革体验。

推荐阅读

Unix Network Programming by Stevens, Fenner and Rudoff.

安全

有关安全的悲惨事实是,大多数安全漏洞来自草率编程。更可悲的事实是,许多学校在培训程序员方面做得很差,没法保护他们的代码。
计算机科学家必须意识到程序可能受到损害的方式。
他们需要培养一种防御性编程感 - 思考如何攻击他们自己的代码。
安全性是一种最好分布在培训的整个课程中:每个学科都应该警告学生本学科的相关漏洞。

具体建议

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

  1. 社会工程学;
  2. 缓冲区溢出;
  3. 整数溢出;
  4. 代码注入漏洞;
  5. 竞争危害;
  6. 权限混淆。

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

推荐阅读

  1. Metasploit: The Penetration Tester's Guide by Kennedy, O'Gorman, Kearns and Aharoni.
  2. Security Engineering by Anderson.

加密

密码学使我们的数字生活成为可能。
计算机科学家应该理解并能够实现以下概念,以及这样做的常见缺陷:

  1. 对称密钥密码系统;
  2. 公钥密码系统;
  3. 安全散列函数;
  4. 询问 - 响应对话认证;
  5. 数字签名算法;
  6. 门限密码体制。

由于这是密码系统实现中的常见错误,因此每个计算机科学家都应该知道如何为手头的任务获取足够随机的数字。
至少,几乎每次数据泄露都表明,计算机科学家需要知道如何对密码进行加密和哈希存储。

具体建议

每个计算机科学家都应该有幸使用前现代密码系统和手工统计工具打破密文。
RSA很容易实现 ,每个人都应该这样做。
每个学生都应该创建自己的数字证书并在apache中设置https。(这样做非常艰巨。)
学生还应该编写一个通过SSL连接的控制台Web客户端。
作为严格的实际问题,计算机科学家应该知道如何使用GPG; 如何对ssh使用公钥认证; 以及如何加密目录或硬盘。

推荐阅读

  1. Cryptography Engineering by Ferguson, Schneier and Kohno.

软件测试

必须在整个课程中使用软件测试。
关于软件工程的课程可以涵盖基本的测试方式,但是没有什么可以替代实践的艺术。
学生应该对他们上交的测试案例进行评分。
我让学生给其他同学写测试用例。
学生似乎并不太关心给自己开发防御性测试案例,但是当他们对其他同学的代码进行测试时,他们会用尽任何手段。

用户体验设计

程序员经常为其他程序员编写软件,或者更糟糕的是为自己编写软件。
用户界面设计(或更广泛地说,用户体验设计)可能是计算机科学中最不被充分认识的方面。
甚至在教授中也存在一种误解,即用户体验是一种无法教授的“软”技能。
实际上,现代用户体验设计是基于人因工程和工业设计的经验的。
如果不出意外,计算机科学家应该知道接口需要能够轻松执行与任务频率成倍增加的任务与其重要性相乘的任务。
实际上,每个程序员都应该习惯于在HTML,CSS和JavaScript中设计可用的Web界面。

推荐阅读

  1. Paul Graham's essay on Web 2.0.
  2. "The Absolute Minimum Every Software
  3. Developer Absolutely, Positively Must Know About Unicode and Character Sets" by Spolsky.
  4. HTML and CSS: Design and Build Websites by Duckett.
  5. JavaScript: The Definitive Guide by Flanagan.

可视化

良好的可视化是以人类将其视为信息的方式呈现数据,这不是一件容易的事。
现代世界是数据的海洋,利用人类感知的局部最大值是理解它的关键。

推荐阅读

  1. The Visual Display of Quantitative Information by Tufte.

并行

并行性又回来了,而且比以往更加丑陋。
不幸的事实是,利用并行性需要深入了解架构:多核,高速缓存,总线,GPU等。
以及,练习,和更多练习。

具体建议

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

软件工程

软件工程中的原理变化与编程语言一样快。
在团队软件构建实践中,一个良好的实践课程提供了工作中固有陷阱的知识。
一些读者建议学生分成三人一组,领导者的角色通过三个不同的项目进行。
学习如何向现有的大型代码库进行攻击和操作,是大多数程序员必须掌握的技能,而且这是在学校而不是在工作中学到的最好的技能。

具体建议

所有学生都需要了解集中版本控制系统如svn,和分布式版本控制系统如git。
调试工具(如gdb和valgrind)的工作知识在熟练前有很长的路要走。

推荐阅读

  1. Version Control by Example by Sink.

形式化方法

随着对安全可靠软件的需求的增加,形式化方法有朝一日可能成为实现它的唯一手段。
目前,软件的形式建模和验证仍然具有挑战性,但该领域的进展是稳定的:每年都变得更加容易。
在今天的计算机科学专业学生的一生中可能会有这么一天,软件形式化方法是一种预期的技能。
每个计算机科学家都应该至少使用一个定理证明程序。(我觉得哪一个不重要。)
学习使用定理证明程序会立即影响编码风格。
例如,一个人对写一个不能涵盖所有可能性的match或switch本能地过敏。
而且,在编写递归函数时,定理证明者的用户强烈要求消除不正当性。

推荐阅读

  1. Software Foundations.

图形和模拟

没有比图形更”俊俏“的学科了。
这个领域被推向,被定义为“足够好了”。
因此,没有比图形和模拟更好的方法来教授对编程或优化工作的理解。
我学到的超过一半的编码黑客技巧来自我对图形的研究。

具体建议

简单的光线跟踪器可以在不到100行代码中构建。
计算在线框3D引擎中执行透视3D投影所需的变换是一种良好的“心理卫生学”。
像BSP树这样的数据结构和像z-buffer渲染这样的算法就是聪明设计的绝佳例子。
在图形和模拟中,还有更多。

推荐阅读

  1. Mathematics for 3D Game Programming and Computer Graphics by Lengyel.

机器人

机器人技术可能是教授入门编程最吸引人的方式之一。
此外,随着机器人技术的成本持续下降,这将使个人机器人革命成为可能。
对于那些可以编程的人来说,难以想象的个人自动化即将出现。

相关文章

  1. Multitouch gesture control for a robot.

人工智能

如果没有其他原因,除了对计算机早期历史的巨大影响,计算机科学家应该研究人工智能。
虽然智能机器的最初梦想似乎遥遥无期,但人工智能激发了许多实际领域,例如机器学习,数据挖掘和自然语言处理。

推荐阅读

  1. Artificial Intelligence by Russell and Norvig.

机器学习

除了出色的技术优势外,相关的职位空缺数量表明,每个计算机科学家都应该掌握机器学习的基础知识。
机器学习加倍强调了解概率和统计的必要性。

具体建议

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

推荐阅读

  1. Machine Learning by Mitchell.

数据库

数据库太常见,太有用而无法忽略。
理解为数据库引擎提供动力的基础数据结构和算法非常有用,因为程序员经常会在更大的软件系统中重新实现数据库系统。
关系代数和关系演算在亚图灵计算模型中脱颖而出,成为非凡的成功案例。
与UML建模不同,ER建模似乎是用于可视化编码软件工件的设计和约束的合理机制。

具体建议

成为可以设置和操作LAMP堆栈的计算机科学家是一个好主意,并且离建立自己的公司还需要付出很多努力。

推荐阅读

  1. SQL and Relational Theory by Date.

非特定阅读建议

Gödel, Escher, Bach by Hofstadter.
Nick Black's advice for MS students.

还有什么?

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

Comments
Write a Comment