分享

学校只教Java的危险性

 联合参谋学院 2014-09-24
                                     ~.~Joel Spolsky   
     如今的孩子变懒了。
     多吃一点苦,又会怎么样呢?
     我一定是变老了,才会这样喋喋不休地这样抱怨和感叹“如今地孩子”,不理解为什么他们不再同意,或者说不再能够做艰苦地工作。
     当我还是孩子地时候,学习编程需要用到穿孔卡片(punched card)。那时可没有任何类似“退格”键(Backspace key)这样地现代化功能,如果你出错了,就没有办法更正,只好扔掉错地卡片,从头再来。
      回想1991年我开始面试程序员地时候。我一般会出一些编程题,允许用任何编程语言解题。在99%地情况下,面试者选者C语言。
      如今,面试者一般会选者Java语言。
      说到这里,不要误会我的意思。作为开发工具,Java语言本身并没有什么错。
      等一等,我要做个更正。我只是在本书特定地环境中不会提到作为一种开发工具Java语言有什么不好地地方。事实上,它有许许多多不好地地方,不过这些只有另找时间来谈了。
      我在这里真正想要说的是,总体来看,Java不是一种非常难的编程语言,无法用来区分优秀程序员和普通程序员。它可能很适合用来完成工作,但是这个不是我今天的主题。我甚至想说,Java语言不够难其实是它的特色,不能算缺点。但是不管怎样,它就是有这个问题。
       如果我听上去像妄下论断,那么我想说一点儿我自己微不足道的经历。大学计算机系的课程历有两个传统的知识点,但许多人从来都没有真正搞懂过,那就是指针(pointer)和递归(recursion)。
       你进大学后,一开始总要上一门“数据结构”课,然后会有链表 散列表以及其他诸如此类的课程。这些课会大量使用“指针”,并且经常起到一种优胜劣汰的作用。因为这些课程非常难,学不会就表明学生的能力不足以达到计算机科学学士学位的要求,这些学生只能选择放弃这个专业。这是一件好事,因为如果你连指针都觉得很难,那么等学到后面要你证明不动点定理(fixed point theory)的时候,你该怎么办呢?
        有些孩子读高中的时候就能用Basic语言在Apple II型个人电脑上写漂亮的乓(Pong)游戏。等他们进了大学都会去选修计算机科学101课程,那门课讲的就是数据结构。当他们接触到指针那些玩意以后,就一下子完全傻眼了,后面的事情你都可以想象:他们就去改学政治学,因为看上去法学院是一个更好的出路。关于计算机系的淘汰率,我见过各式各样的数字,通常在40%到70%之间。校方一般会觉得学生拿不到学位很可惜,我则视其为必要的筛选,淘汰那些没有兴趣编程或者没有能力编程的人。
       对于许多计算机系的青年学生来说,另一门有难度的课程是有关函数式编程(functional programming)的,其中就包括递归程序设计(recursive programming)。麻省理工学院将这些课程的标准提得很高,还专门设立了一门必修课(6.001),它的教材(Stucture and Interpretation of Computer Programs,作者为Harold Abelson等,中文版书名为《计算机程序的构造和解释》)被几百所高校的计算机系采用,充当事实上的计算机科学导论课程。(你能在网上找到这门课程的视频,应该看一下){http://groups.csail./mac/classes/6.001/abelson-sussman-lectures/}。
        这些课程难得惊人。在第一堂课,你就要学完Scheme语言的几乎所有内容,(Scheme语言是Lisp语言的一个变种,业内人士都称为Lisp的方言。于1975年诞生于麻省理工学院,以其对函数式编程的支持而闻名。)你还会遇到一个不动点函数,它的参数本身就是另一个函数。我读的这门导论课式宾夕法尼亚大学的CSE121课程,真是读得苦不堪言。我注意到很多学生(也许式大部分的学生)都无法完成这门课。课程的内容实在太难了。我给教授写了一封长长的声泪俱下的Email,控诉这门课不是给人学的。宾夕法尼亚大学里一定有人听到了我的呼声(或者听到了其他抱怨者的呼声)。因为如今这门课讲授的计算机语言是Java。
       我现在觉得,他们还不如没有听见呢!
       这就是争议所在。许多年来,像当年的我一样懒惰的计算机系本科生不停地抱怨,再加上计算机业界也在抱怨毕业生不够用,这一切终于造成了重大恶果。过去十年中,大量本来堪称完美地学校,都百分百转向了Java语言地怀抱。这真是好的没话说了,那些用grep命令(UNIX/Linux环境中用于搜索或者过滤内容地命令。)过滤简历地企业招聘主管,大概会很喜欢这样。最妙不可言的是,Java语言中没有什么太难的地方,不会真地淘汰什么人,你搞不懂指针或者递归页没关系。所以,计算机系地淘汰率就降低了,学生人数上升了,经费预算变大了,可谓皆大欢喜。
        学习Java语言饿孩子是幸运地,因为当他们用到以指针为基础的散列表时,他们永远也不会遇到古怪的“段错误”(segfault是segmentation fault的缩写){指的是软件中的一类特定错误,通常发生在程序试图读取不允许读取的内存地址或者以非法方式读取内存的时候。}他们永远不会因为无法将数据塞进有限的内存空间而急得发疯,他们也永远不用苦苦思索,为什么在一个纯函数的程序中,一个变量的值一会儿保持不变,一会儿又变个不停!多么自相矛盾啊!
        他们不需要怎么动脑筋就可以在专业课上得到4.0的成绩。
        我是不是有点太苛刻了?就像电视里的“四个约克郡男人”{《四个约克郡男人》(Four Yorkshiremen)是英国电视系列喜剧At last the 1948 Show中的一部,于20世纪70年代播出。内容是四个约克郡男人竞相吹嘘各自的童年是多么困苦。由于内容太夸张,所以显得非常可笑。}那样,成了老古板?就知道在这里吹嘘我是多么刻苦,完成了所有那些高难度的课程?
        我再告诉你一件事。1900年的时候,拉丁语和希腊语都是大学里必修课,原因不是因为它们有什么特别作用,而是因为它们有点被看成是受过高等教育的人士的标志。在某种程度上,我的观点同拉丁语支持者的观点没有不同(下面的四点理由都是如此):“(拉丁语)训练你的思维,锻炼你的记忆。分析拉丁语的句法结构是思维能力的最佳练习,是真正对智力的挑战,能够很好地培养逻辑能力。”以上出自Scott Barker之口(http://www./whylatin.htm)。但是,今天我找不到一所大学还把拉丁语作为必修课。指针和递归不正像计算机科学中地拉丁语和希腊语吗?
        说到这里,我坦率地承认,当今地软件代码中90%都不需要使用指针。事实上,如果在正是产品中使用指针。这将是非常危险地。好的,这一点没有异议。与此同时,函数式编程在实际开发中用到地也不多。这一点我也同意。
       但是,对于某些最激动人心地编程任务来说,指针仍然是非常重要地。比如说,如果不用指针,你根本没办法开发Linux地内核。如果你不是真正地理解了指针,你连一行Linux的代码页看不懂,说实话,任何操作系统的代码你都看不懂。
      如果你不懂函数式编程,你就无法创造出MapReduce(是一种由Goolge引入使用的软件框架,用于支持计算机集群环境下海量数据PB级别的并行计算。),正是这种算法使得Google的可扩展性(scalable)达到如此巨大的规模。术语“Map”(映射)和“Reduce”(化简)分别来自Lisp语言和函数式编程。回想起来,在类似6.001这样的编程课程中,都提到纯粹的函数式编程没有副作用,因此可以直接用于并行计算。任何人只要还记得这些内容,那么MapReduce对他来说就是显而易见的。发明MapReduce的公司是谷歌,而不是微软,这个简单的事实说出了原因,为什么微软至今还在追赶,还在试图提供最基本的搜索服务,而谷歌已经转向了下一阶段,开发Skynet,我的意思是,开发世界上最大的并行式超级计算机。我觉得,微软并没有完全明白在这波竞争中它落后了多远。
        除了上面那些直接就能看到的重要性,指针和递归的真正价值在于那种你在学习它们的过程中所得到的思维深度,以及你因为害怕在这些课程中被淘汰所产生的心理抗压能力,它们都是在建造大型系统的过程中必不可少的。指针和递归要求一定水平的推理能力 抽象思考能力,以及最重要的,在若干个不同的抽象层次上同时审视同一个问题的能力。因此,是否真正理解指针和递归与是否是一个优秀程序员直接相关。
       如果计算机系的课程都与Java语言有关,那么对于那些在智力上无法应付复杂概念的学生而言,就没有东西可以真地淘汰他们。作为一个雇主,我发现那些100%Java教学的计算机系已经培养出了相当一大批毕业生,这些学生只能勉强完成难度日益降低的课程作业,只会用Java语言编写简单的记帐程序,如果你让他们编写一个更难的东西,他们就束手无策了。他们的智力不足以成为程序员。这些学生永远页通不过麻省理工学院的6.001课程,或者耶鲁大学的CS 323课程。坦率地说,为什么在一个雇主的心目中,麻省理工学院或者耶鲁大学计算机系的学位的份量要重于杜克大学的,这就是原因之一。因为杜克大学最近已经全部转为用Java语言教学。宾夕法尼亚大学的情况也很类似,当初CSE 121课程中的Scheme语言和ML语言几乎将我和我的同学折磨致死,如今已经全部被Java语言代替。我的意思不是说我不想雇用来自杜克大学或者宾夕法尼亚大学的聪明学生,我真地愿意雇用他们,只是对于我来说,确定他们是否真地聪明如今变得难多了。以前,我能够分辨出谁是聪明学生,因为他们可以在一分钟内看懂一个递归算法,或者可以迅速在计算机上实现一个线性链表操作函数,所用的时间同在黑板上写一遍这个函数差不多。但是对于在学校只学Java语言的毕业生,看着他们面对上述问题苦苦思索却做不出来的样子,我分辨不出这到底是因为学校里没教,还是因为他们不具备编写优秀软件的素质。Paul Graham(Steve Soong(宋易峰)强烈建议读一读他的一本叫《黑客和画家》的书,很不错哟!)将这一类程序员称为“Blub程序员”{Blub程序员(Blub programmers)指的是那些企图用一种语言解决所有问题的程序员。Blub是Paul Graham假设的一种高级编程语言}[www.pualgraham.com/avg.htm]
        大学里只教Java语言,无法淘汰那些永远也成不了优秀程序员的学生,这已经是很糟糕的事情了。但是,学校可以无可厚非地辩解,这不是校方地错。整个软件行业,或者说至少是其中那些用grep命令过滤简历地招聘经理,确实是在一直叫嚷,要求学校使用Java语言教学。
        但是,即使如此,学校地教学也还是失败地,因为学校没有成功训练好学生地头脑,没有使他们变得足够熟练 敏捷 灵活,能够做出高质量地软件设计。(我不是指面向对象式的“设计”,那种编程只不过式要求你花上无数个小时重写你的代码,使它们能够满足面向对象编程的等级制继承式结构,或者说要求你思考到对象之间的式“has-a”从属关系,还是“is-a”继承关系,这种“伪命题”将你搞得烦躁不安。)你需要的式那种能够在多个抽象层次上同时思考问题的训练。这种思维能力正是设计出优秀软件架构所必需的。
       你也许想知道,在教学中OOP(Obiect-Oriented Programming,面向对象编程)是否是指针和递归的优质替代品,是不是也能起到淘汰作用。简单回答是“不”。我在这里不讨论OOP的优点,我只指出OOP不够难,无法淘汰平庸的程序员。大多数时候,OOP教学的主要内容就是记住一堆专有名词,比如“封装”(encapsulation)和“继承”(inheritance),然后在做一堆多选题小测验,考你是不是明白“多态”(polymorphism)和“重载”(overloading)的区别。这同历史课上要求你记住重要的日期和人名的难度差不多。OOP不构成堆智力的太大挑战,吓不跑一年级新生。据说,如果你没学好OOP,你的程序依然可以运行,只是维护起来有点难。但是如果你没学好指针,然后你只好停下来,深呼一口,真正开始努力在两个不同的抽象层次上同时思考你的程序是如何运行的。
       顺便说一句,我有充分理由在这里说,那些使用grep命令过滤简历的招聘经理真是荒谬可笑。我从来没有见过哪个能用Scheme语言 Haskell语言和C语言中的指针编程的人,竟然不会在两天里面学会Java语言,并且写出的Java语言的质量竟然不能胜过那些有5年Java编程经验的人士。不过,是无法指望人力资源部里那些平庸的懒汉听进去这些话的。
       再说,计算机系承担的发扬光大计算机科学饿使命该怎么办呢?计算机系毕竟不是职业学校啊!训练学生如何在这个行业里工作不应该是计算机系的任务,这应该是社区高校和政府就业培训计划的任务,那些地方会教给你工作技能。计算机系给予学生的理应是他们日后生活所需要的基础知识,而不是为学生第一周上班做准备。对不对?
       还有,计算机科学是由证明(递归)、算法(递归)、语言(入演算)、操作系统(指针)、编译器(入演算)所组成的,所以说那些不教C语言 、不教Scheme语言 、只教Java语言的学校实际上根本不是在教授计算机科学。虽然对于真实世界来说,有些概念可能毫无用处,比如函数的科里化(function currying指的是一种多元函数的消元技巧,将其变为一系列只有一元的链式函数。它最早是由美国数学家哈斯格尔 科里 Haskell Curry 提出的,因此而得名。)但是这些知识显然是进入计算机科学研究生院的前提。我不明白,计算机系课程设置委员会中的教授为什么会同意将课程的难度下降到如此低的地步,以至于他们既无法培养出合格的程序员,甚至无法培养出合格的能够得到哲学博士学位 进而能够申请教职 与他们竞争工作岗位饿研究员。噢,且慢,我说错了。也许我明白原因了。
        实际上,如果你回顾和学术界在“Java大迁移(Great Java Shift)中的争论,你会注意到,最大的议题是Java语言是否还不够简单,不适合作为一种教学语言。
       我的老天啊,我心里说,他们还在设法让课程变得更简单。为什么不干脆用匙子把所有东西一勺勺都喂到学生嘴里呢?让我们再请助教帮他们接管考试,这样一来就没有会该学“美国研究”了(是对美国社会的经济 历史 文化等各个方面进行研究的一门学科。这里指的是,计算机系学生不会因为课程太难被淘汰,所以就不用改学相对容易的“美国研究”)。如果课程被精心设计,使得所有内容都比原有内容更容易,那么怎么可能期望任何人从这个地方学到任何东西呢?看上去似乎有一个工作小组(Java task force)正在开展工作,创造出一个简化的文档,小心地不让学生纤弱的思想接触到任何EJB/J2EE的脏东西。这样一来,学生的小脑袋就不会因为遇到有点难度的课程而感到烦恼了,除非那门课里只要求做一些空前简单的计算机习题。
       计算机系如此积极地降低课程难度,有一个最善意地解释,那就是节省出更多地时间去教授真正属于计算机科学地概念。但是,前提是不能花费整整两节课向学生讲解如Java语言中int和Integer有何区别。好的,如果真是这样,课程6.001就是你地完美选择。你可以先讲Scheme语言,这种教学语言简单到聪明学生大约只用10分钟就能全部学会地程度。然后,你将这个学期剩下的时间都用来讲解不动点。
       诶。
       说了半天,我还是在说要学1和0.
     (你是1?真幸运啊!我们那时所有人得到的都是0。) 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多