爪哇学校的危害

By Joel Spolsky Thursday, December 29, 2005

懒惰的孩子。

辛勤工作这回事是怎么了?

一直发牢骚抱怨「现在的小孩」,怪他们不愿亦不能做较困难的事情,这显然表示我已逐渐老朽了。


你们很幸福啦,以前我们在化粪池里一个牛皮纸袋里住了三个月,早晨六点就得起床把袋子清乾净,吃一块过期面包皮就开始工作(work down the mill?),日复一日每天工作十四小时,回到家后父亲还会拿皮带把我们轰去睡觉。

--Monty Python的飞行马戏团, Four Yorkshiremen


当我还是个孩子的时候,我使用打孔卡片来学习编写程式(译注:最早的编程是先在卡片上打孔,然后让电脑一一读入处理),假若你出错了,很抱歉,没什么时髦功能可以补救(比如按倒退键来修正),只能扔了这张卡片然后重新开始。

我在1991年开始面试程式员时,通常会让他们自行选择语言来解决我所出的编程问题。99%的机会他们会选择C。

现在,他们倾向于选择Java。

不要误解我:以实作语言而言,Java并没有任何问题。

等一下,我要修正一下。我并不是要在_这篇文章_ 中声称,Java就实作语言而言没有任何问题,事实上它不对的地方可多了,不过那得等我写另外一篇文章来谈了。

我真正要说的是,整体来看Java这种语言并不够困难,无法区分伟大的程式员跟平庸的程式员。在工作上或许是个好语言,但这不是今天的重点。我什至会进一步说,Java不够难这件事是个特色而非缺陷,可是它毕竟还是有这个问题。

这样说可能很傲慢,不过就我卑微的经验而言,指标跟递回是大学资讯科​​学传统课程中,许多人永远无法完全理解的两件事。

大学课程通常由资料结构这门课开始,里面有链结串列(linked lists)和杂凑表(hash tables)等等都广泛的使用指标。这些课程常被用来作为淘汰课程:它们非常困难,会让无法面对CS(资讯科学,后面都简称CS)学位心智挑战的人选择放弃。这是件好事,因为如果你认为指标很难,等到要证明不动点定理(fixed point theory)的题目时就知道什么叫难了。

有些小孩高中就在自己的Apple II上,用BASIC写出很棒的乒乓游戏。当这些孩子上大学修CompSci 101(一门资料结构的课程),接触指标这玩意之后,轰!他们的大脑马上被炸成一堆豆腐渣,接下来都跑去修政治学,因为法学院似乎是个更好的主意。我看过CS科系退学率的各种数字,通常介于40%到70%之间。大学当局通常认视之为浪费,我则认为这是必要的排除手段,让那些往后无法乐于编程职业或因此成功的人及早出局。

对许多年轻的CS学生来说,另一个难题是教授functional programming(译注:请参考[wiki](http://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B8%E5%BC%8F%E7%B7%A8%E7%A8 %8B))(包括递回编程在内)的课程。MIT对这些课程的要求很高,不但创造了必修课程(6.001),还编写出一本教科书(Abelson & Sussman的[Structure and Interpretation of Computer Programs](http://mitpress.mit.edu/sicp/full- text/book/book.html)),被数十甚至数百所一流CS学校拿来作为CS入门教材。(你可以也应该在[线上](http://swiss.csail.mit.edu/classes/6.001/abelson- sussman-lectures/)看看旧版的教材。)

这些课程的困难度极高。第一讲就几乎教完Scheme所有的内容(译注:Scheme是一种古老的程式语言,常见于AI领域),还介绍了把另一个函式当成输入的固定点函式。在与这种课程(宾州大学的CSE121)奋斗之际,我注意到很多学生(可以说是大多数)都撑不过。教材实在是太艰涩了。我写了一封声泪俱下的长篇电邮给教授,控诉 这门课实在是不人道 !宾州大学一定有人听到了我(或其他抱怨者)的心声,因为这门课现在已经改用Java授课了。

我真希望他们没有听到这个心声。


你认为自己搞懂了吗?在这儿测测你自己


这就是争议所在。像我这样多年来发牢骚责怪懒惰的CS大学生,再加上业界抱怨主修CS的美国大学毕业生不足,已经对整体环境造成了极大的伤害。过去十年间大量原本很优秀的学校已经完全转向Java。这真是赞啊,用grep(译注:这是UNIX环境上常见用来过滤/搜寻档案内容的程式)过滤履历的征才人员应该挺高兴的(译注:因为有一堆人可供他们选择了);而且最棒的是,Java里没有什么很难的东西,不能真正淘汰掉那些没有指标跟递回脑细胞的程式员,所以退学率降低,于是CS系所收的学生更多,预算也水涨船高,一切是如此的美好。

爪哇学校教出出来的幸褔小孩不需要用指标实作杂凑表,因此从未遭遇segfaults(译注:segmentation faults的缩写)这种错误。他们从不需要战战兢兢、发疯似地试着把东西塞进极有限的空间中。他们也永远不需要动脑想想,为何在纯粹functional程式中,一个变数的值永不改变,但却又随时在变!一种自相矛盾的东西!

他们不需要可以让他们在学位上获得4分的那部分脑细胞。

我就是像Four Yorkshiremen那样的老古板吗?才会一直吹嘘自己以前多么坚韧,能安然渡过这么困难的课程吗?

嗨!在1900年,拉丁文跟希腊语是大学必修的课程。并不是因为它们能做什么,而是它们多少被视为受过教育的人所必备的知识。在某些概念上,我的论点跟支持学拉丁文的人没什么不同(以下四个理由都是):「(拉丁文)训练你的心智。训练你的记忆力。阐明一个拉丁文句子可锻炼思考能力,是一个真正智力上的考验,并且对逻辑思考的良好起点」Scott Barker如是。不过我现在已经找不到任何还要必修拉丁文的大学了。那么指标跟递回算是CS的拉丁与希腊语吗?

现在我要坦率地承认,如今写的程式九成都不需要用到指标,而且事实上在产品程式上使用指标相当危险。是的,没关系。而functional programming在实务上几乎都没人在用,这我也同意。

不过它在某些最刺激的编程作业上依然有其重要性。举例来说,不用指标根本无法在Linux核心做事。假若你并不是真的完全了解指标,就无法理解任何一行Linux(甚至是任何作业系统)的程式码。

不了解function programming,就无法发明MapReduce这个让Google具备惊人扩充性的演算法。Map跟Reduce是从Lisp和function programming来的。修过相当于6.001编程课程的人,如果还记得纯functional programs没有什么副作用,不会影响并行的函式,因此可以轻易地平行化同步处理。在他们回想之际,MapReduce实是是显而易见的演算法。Google发明了MapReduce而微软没有,这个事实可以解释为何当微软仍在尝试让基本搜寻功能能运作时,Google已经进到下个领域:建立[Skynet](http://en.wikipedia.org/wiki /Skynet)这个世界上最大的平行处理超级电脑,我不认为微软真正了解他们在这波竞争中落后了多少。

但是在这些表像的重要性背后,指标和递回有其真正的价值。在学习它们的过程中,能获得建造大型系统所需的心智复杂度,以及免于被相关课程淘汰的特殊资质。必须具备某种能力,才能理解并以抽象的方式思考指标和递回,而最重要的就是同时从数个不同的抽象层次看待同一个问题。因此,了解指标跟递回的能力与成为伟大程式员的能力有着直接的关连。

在一个纯Java的CS学位之中,没有东西能淘汰缺乏处理这些概念的灵活心智的学生。身为一个雇主,我看到纯爪哇学校已经开始量产出一些根本不够聪明的CS毕业生,尽管他们可以勉强通过那些新简化过的课程作业,却不足以进行任何比「又一个Java会计管理程式」更复杂的工作。这些学生永远无法通过麻省理工学院的6.001或耶鲁的CS 323课程,坦白说,这就是雇主心目中,麻省理工学院或耶鲁的CS学位比杜克大学的CS学位(已经完全转向Java)更有份量的原因之一。宾州大学也放弃Scheme和ML语言(译注:此两种语言常见于AI领域上),尝试用Jave来教授那门几乎整死我和我朋友的CSE121课程。并不是说我不会雇用来自杜克或宾州大学的聪明孩子,我会的。只是对我来说,要分辨谁够聪明要难上许多。过去我能分辨出聪明的孩子,因为他们能瞬间解通一个递回的演算法,而且用指标实作键结串列的速度和在白板上写一样快。但是当爪哇学校的毕业生卡在这些问题时,我无法分辨是因为他们受的教育不足,或是他们根本缺少进行庞大编程工程所需的天赋。Paul Graham管他们叫做Blub程式员

爪哇学校不能淘汰这些永远无法成为伟大程序员的孩子,这已经是很糟糕的事,可是学校可以辨称这不是他们的问题,但业界(至少那些用grep筛选履历的招募人员是如此)却是确实地吵着学校必须教授Java课程。

然而爪哇学校也无法训练孩子们的脑袋更熟练、聪颖、灵活,让他们能完成良好的软体设计(我不是指那种花费无数时间改写程式码来重调继承架构,或是为了has- a还是is-a之类的假性「问题」烦恼的OO「设计」)。你需要训练才能同时在多个抽象层次上思考事情,而在设计庞大的软体架构时正需要这种思考能力。

你或许会想知道,物件导向编程(OOP)是否能取代指标跟递回,成为良好的淘汰工具。简单的答案是「否」。撇开OOP的功过不论,它就是没有难到足以淘汰平庸的程式员。学校教的OOP绝大多数都是在背诵一堆像「封装」、「继承」之类的词汇,再做些多型与多载(overload)间差异的单选题小测验。OOP并不比在历史课上背诵重要日期和名字难很多,而是用不适当的心智挑战吓跑一年级学生。据称当你面对OOP问题时,程式仍然能工作,只是有点难维护而已。但是当你与指标问题奋战时,程式只会产生一行 Segmentatidon Fault ,除非你停下来深呼吸,然后真正试着集中精神同时在两个不同抽象层次进行思考,否则永远不知道发生了什么事。

另外我有很好的理由嘲笑那些使用关键字过滤履历的面试者。我从未看过哪个会用Scheme、Haskell和C指标的人,不能在两天内熟悉Java并写出超越5年Java经验老手的程式码。不过跟一般人力资源部门的懒人解释似乎是徒劳无功的。

但CS学院的CS任务该怎么办呢?他们并不是职业学校呀!训练人们到业界工作并不应该是他们的任务。他们会说这是由社区大学和针对转职工作者的政府再教育计画处理的。他们应该是提供基本工具让学生可以生活,而不是为他们第一周上班作准备。不是吗?

Cs1.png

打孔机– 没错,我12岁时在这种机器上学写Fortran.

还有,CS关乎证明(递回)、演算法(递回)、语言(lambda 演算)(译注:lambda calculus,为一套数学理论,请参考wiki)、作业系统(指标) 、编译器(lambda 演算),所以不教C和Scheme课程的爪哇学校其实也没有在教资讯科学。对于真实世界来说,函式curry化(译注:function currying是一个数学方法,请参考[wiki](http://zh.wikipedia.org/w/index.php?title=Currying&variant=zh- tw))的概念或许对真实世界没什么用,但对CS研究所来说显然是必要资格。我不能理解为何CS学校课程委员会的教授,竟然会让他们的课程沉沦至此,不仅无法产生 有用的程式员 ,甚至不能产生可能获得博士学位并与他们竞逐教职的CS研究生。噢,等等,或许我真的了解原因了。

事实上当你回头研究学术界在Java狂热时代所进行的讨论,就会注意到最大的议题为Java是否_简单_ 到足以作为一种教学语言。

我的天呀,他们正试着让课程更进一步的简化 ,我这样想着。为何不干脆把所有东西都用汤匙喂给学生算了?让我们再请助教帮他们写考卷,如此就不会有人落跑到美国研究去了(译注:American Studies,研究美国文化、社会等等的单位)。如果课程已被仔细地设计,让所有东西都变得比原本更容易,怎能祈求有任何人可以从这学到任何事情?看起来似乎有个工作小组会在进行某个任务([PDF文件](http://www.sigcse.org/topics/javataskforce/java- task- force.pdf)),想要找出某个简化后的Java子集来教育学生,这个计画制作简化的文件,小心地隐藏所有EJB/J2EE废话不让学生稚嫩的心灵接触,因此不再需要让他们的小脑袋去担心任何连简单无比的CS问题集都不用做的课程。

至于CS系所为何如此执着于简化课程,最好的解释是这样能空出更多时间来教授真正的CS观念,不过这得假设不需要花两堂课对学生说明Java intInteger 间的差异。嗯,假设真是如此,6.001课程可以给你一个完美的答案:Scheme,一个教学用语言,它是如此的简单,对聪明的学生来说十分钟内就可以教会,然后你可以用剩下的整个学期去教固定点理论。

哈!

我要回到0与1的世界了。

(你有1吗?你幸运的混蛋!我们拿到的全是0)


你是个大三学生吗?而且能瞬间弄通一个递回演算法,或用指标实作键结串列的速度和在白板上写一样快吗?看看我们在纽约的[暑期实习]!到期日是二月一日。