花十年时间,教自己学会编程

作者:Peter Norvig

为什么人人都如此行色匆匆?

走进任何一家书店,你都会看到《24小时自学Java》之类的书,旁边还有无数类似的、号称能在几天或几小时内教会你C、SQL、Ruby、算法等等的速成指南。我在亚马逊上用[书名包含:teach, yourself, hours, 2000年以来]这样的条件进行高级搜索,找到了512本这样的书。排名前十的书中,有九本是关于编程的(另一本是关于记账的)。如果把“teach yourself”(自学)换成“learn”(学习),或者把“hours”(小时)换成“days”(天),结果也大同小异。

结论是:要么是人们急于学习编程,要么是编程比其他任何事情都容易学得多。Felleisen等人在他们的著作《如何设计程序》中也提到了这一趋势,他们说:“糟糕的编程很容易。白痴都能在21天内学会,即使他们是傻瓜。”网络漫画“不经意的鹅”(Abtruse Goose)也对此发表了自己的看法。

我们来分析一下,像《24小时自学C++》这样的书名到底意味着什么:

  • 自学:在24小时内,你根本没时间写几个像样的程序,并从成功和失败中吸取教训。你没时间与经验丰富的程序员共事,去理解在C++环境中工作是怎样的体验。简而言之,你没时间学到太多东西。所以,这本书能教给你的,只能是浮光掠影的了解,而非深刻的理解。正如亚历山大·蒲柏所说:一知半解是件危险的事(a little learning is a dangerous thing)。
  • C++:在24小时内,你或许能学会C++的一些语法(前提是你已经了解另一门语言),但你不可能学到太多关于如何运用这门语言的知识。简单来说,假如你是个Basic程序员,你或许能学会用C++的语法写出Basic风格的程序,但你学不到C++真正的优点(和缺点)是什么。那意义何在呢?Alan Perlis曾说:“一门不改变你对编程看法的语言,不值得去学”。当然,也有一种可能,你只是为了用某个现有工具完成一项特定任务,而需要了解一点点C++(或者更有可能是JavaScript或Processing)。但这样的话,你学的不是如何编程,你学的是如何完成那项任务。
  • 在24小时内:很不幸,这时间远远不够,下一节会告诉你为什么。

花十年时间,教自己学会编程

研究人员(Bloom (1985),Bryan & Harter (1899),Hayes (1989),Simmon & Chase (1973))已经表明,要在任何一个领域达到专家水平,大约需要十年时间,这些领域涵盖了国际象棋、音乐创作、电报操作、绘画、钢琴演奏、游泳、网球,以及神经心理学和拓扑学的研究。其中的关键是刻意练习(deliberative practice):不仅仅是日复一日地重复,而是要不断挑战稍高于自己目前能力水平的任务,去尝试,在练习中和练习后分析自己的表现,并修正错误。然后重复,再重复。似乎没有什么真正的捷径:即使是莫扎特,这位4岁就展露音乐天赋的神童,也花了超过13年的时间才开始创作出世界级的音乐。在另一个领域,披头士乐队(The Beatles)在1964年登上埃德·沙利文秀,并凭借一连串冠军单曲一炮而红,仿佛一夜成名。但实际上,他们从1957年起就在利物浦和汉堡的小俱乐部里演出了。虽然他们很早就获得了大众的喜爱,但他们第一张备受评论界赞誉的伟大专辑《Sgt. Peppers》是在1967年才发行的。

马尔科姆·格拉德威尔(Malcolm Gladwell)推广了这个观点,尽管他更关注的是10000小时,而不是10年。亨利·卡蒂埃-布列松(Henri Cartier-Bresson, 1908-2004)有另一个衡量标准:“你拍的一万张照片,是你最差的一万张。”(他没料到,在数码相机时代,有些人一周就能拍到这个数。)真正的专业技能可能需要一生的时间来磨练:塞缪尔·约翰逊(Samuel Johnson, 1709-1784)说:“任何领域的卓越,都只能通过一生的劳动来获得;无法用更低的代价购得。”乔叟(Chaucer, 1340-1400)也曾抱怨:“生命如此短暂,技艺却需要如此长的时间来学习(the lyf so short, the craft so long to lerne)。”希波克拉底(Hippocrates, c. 400BC)的那句名言“ars longa, vita brevis”更为人熟知,它出自一段更长的话:“Ars longa, vita brevis, occasio praeceps, experimentum periculosum, iudicium difficile”,翻译成英文是“Life is short, [the] craft long, opportunity fleeting, experiment treacherous, judgment difficult.”(生命短暂,技艺永存,机会转瞬即逝,尝试充满风险,判断异常困难。)当然,没有任何一个数字可以作为最终答案:我们没理由认为所有技能(比如编程、国际象棋、西洋跳棋和音乐演奏)都需要完全相同的时间来掌握,也不能假设每个人花费的时间都完全一样。正如K. Anders Ericsson教授所说:“在大多数领域,即使是最有天赋的人,要达到最高水平的表现也需要惊人的时间投入。10000小时这个数字只是让你有个概念,我们谈论的是长达数年、每周投入10到20小时的练习,即使是那些被某些人认为天赋异禀的个体,也仍然需要这么多时间才能达到顶尖水平。”

那么,你想成为一名程序员

这是我为编程成功开出的“秘方”:

  • 培养兴趣,找点乐子去编程。确保编程的乐趣足以让你愿意投入那十年/一万小时。
  • 动手编程。最好的学习方式是在实践中学习。用更专业的话来说,“个体在特定领域的最高表现水平,并不会随着经验的增加而自动获得,但即使是经验非常丰富的人,也可以通过刻意努力来提高表现水平。”(p. 366)并且,“最有效的学习需要一个明确定义的、难度适合特定个体的任务,并提供信息丰富的反馈,以及重复和纠正错误的机会。” (p. 20-21) 《实践中的认知:日常生活中的思维、数学和文化》这本书对这个观点有有趣的论述。
  • 与其他程序员交流;阅读他人的代码。这比任何书籍或培训课程都重要。
  • 如果你愿意,花四年时间上个大学(或者在研究生院花更长时间)。这能让你获得一些需要学历的工作机会,也能让你对这个领域有更深入的了解。但如果你不喜欢学校,你也可以(通过一些努力)靠自学或在工作中获得类似的经验。无论如何,单靠书本知识是不够的。“计算机科学教育无法让任何人成为专家程序员,就像研究画笔和颜料无法让任何人成为专家画家一样,”《新黑客词典》(The New Hacker's Dictionary)的作者埃里克·雷蒙德(Eric Raymond)如是说。我雇过的最优秀的程序员之一只有高中学历;他写出了很多伟大的软件,有自己的新闻组,并通过股票期权赚的钱买下了自己的夜总会。
  • 与他人合作项目。在一些项目中,你要做最牛的那个;在另一些项目中,你要做最菜的那个。当你最牛时,你可以锻炼自己领导项目和用愿景激励他人的能力。当你最菜时,你可以学习大师们是怎么做的,还能学到他们不喜欢做什么(因为他们会让你去做那些事)。
  • 接手他人的项目。去理解别人写的程序。看看在原作者不在场的情况下,理解和修复它需要付出什么。思考如何设计你自己的程序,以便让那些在你之后维护它的人更容易上手。
  • 学习至少六种编程语言。包括一门强调类抽象的语言(如Java或C++),一门强调函数式抽象的语言(如Lisp、ML或Haskell),一门支持语法抽象的语言(如Lisp),一门支持声明式规范的语言(如Prolog或C++模板),以及一门强调并发的语言(如Clojure或Go)。
  • 记住“计算机科学”(computer science)里有“计算机”(computer)这个词。了解你的计算机执行一条指令、从内存中获取一个字(有缓存命中和未命中的情况)、从磁盘连续读取数据,以及在磁盘上寻道到一个新位置需要多长时间。(答案在这里)
  • 参与一项语言标准化工作。可以是ANSI C++委员会,也可以是决定你团队的编码风格是用2个还是4个空格缩进。无论哪种方式,你都能了解到其他人对一门语言的喜好,他们对此有多么执着,甚至还能稍微理解他们为什么会有这种感觉。
  • 要有明智的判断力,尽快从语言标准化工作中脱身。

考虑到所有这些,仅靠书本学习能走多远是很值得怀疑的。在我第一个孩子出生前,我读了所有“如何做”的育儿书,但仍然感觉自己像个一无所知的新手。30个月后,当我的第二个孩子即将出生时,我回去重温那些书了吗?没有。相反,我依靠自己的亲身经历,结果发现这比专家们写的数千页文字有用得多,也让我安心得多。

弗雷德·布鲁克斯(Fred Brooks)在他的文章《没有银弹》中,提出了一个寻找优秀软件设计师的三步计划:

  1. 尽早系统地识别顶尖的设计师苗子。
  2. 指派一位职业导师,负责这位潜力人才的发展,并仔细保管其职业档案。
  3. 为成长中的设计师提供相互交流和激励的机会。

这个计划的前提是,有些人已经具备了成为伟大设计师的必要品质;我们的工作只是适当地引导他们。Alan Perlis说得更简洁:“每个人都可以被教会雕塑,但米开朗基罗却需要被教导如何不去雕塑。伟大的程序员也是如此。”Perlis的意思是,伟人身上有一种超越训练的内在品质。但这种品质从何而来?是天生的吗?还是他们通过勤奋培养出来的?就像《美食总动员》(Ratatouille)里的虚拟厨神奥古斯特·古斯特(Auguste Gusteau)所说:“人人都能烹饪,但只有无所畏惧的人才能成为伟人。”我认为,这更多地是指一个人愿意将生命中的大部分时间投入到刻意练习中。但或许“无所畏惧”(fearless)正是对这种精神的概括。或者,正如古斯特的评论家安东·伊戈(Anton Ego)所说:“不是每个人都能成为伟大的艺术家,但伟大的艺术家可以来自任何地方。”

所以,去买那本Java/Ruby/Javascript/PHP的书吧;你可能会从中得到一些用处。但在24小时或21天内,你不可能改变你的生活,也不可能改变你作为程序员的真正综合实力。那么,花24个月努力持续进步怎么样?嗯,现在你开始有点上道了……

参考文献

Bloom, Benjamin (ed.)Developing Talent in Young People(《培养年轻人的才能》), Ballantine, 1985.

Brooks, Fred,No Silver Bullets(《没有银弹》), IEEE Computer, vol. 20, no. 4, 1987, p. 10-19.

Bryan, W.L. & Harter, N. "Studies on the telegraphic language: The acquisition of a hierarchy of habits.Psychology Review, 1899, 8, 345-375

Hayes, John R.,Complete Problem Solver(《问题解决大全》) Lawrence Erlbaum, 1989.

Chase, William G. & Simon, Herbert A."Perception in Chess"Cognitive Psychology, 1973, 4, 55-81.

Lave, Jean,Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life(《实践中的认知:日常生活中的思维、数学和文化》), Cambridge University Press, 1988.

参考答案

一台典型个人电脑上各种操作的大致耗时:

操作

时间

执行一条典型指令

1/1,000,000,000 秒 = 1 纳秒

从 L1 缓存读取

0.5 纳秒

分支预测错误

5 纳秒

从 L2 缓存读取

7 纳秒

互斥锁加锁/解锁

25 纳秒

从主内存读取

100 纳秒

通过 1Gbps 网络发送 2K 字节

20,000 纳秒

从内存顺序读取 1MB

250,000 纳秒

从硬盘新位置读取(寻道)

8,000,000 纳秒

从硬盘顺序读取 1MB

20,000,000 纳秒

数据包从美国到欧洲再返回

150 毫秒 = 150,000,000 纳秒

附录:语言的选择

好几个人问我应该先学哪门编程语言。没有唯一的答案,但可以考虑以下几点:

  • 听朋友的。当有人问我“我应该用哪个操作系统,Windows、Unix还是Mac?”,我的回答通常是:“用你朋友们在用的那个。”从朋友那里获得的帮助,会抵消不同操作系统或不同编程语言之间的任何内在差异。还要考虑你未来的朋友:如果你继续走下去,你将成为哪个程序员社区的一员。你选择的语言是拥有一个庞大且不断增长的社区,还是一个小而日渐式微的社区?有没有书籍、网站和在线论坛可以让你找到答案?你喜欢那些论坛里的人吗?
  • 保持简单。像C++和Java这样的编程语言是为大型团队中的资深程序员进行专业开发而设计的,他们非常关心代码的运行时效率。因此,这些语言中包含了为这些情况设计的复杂部分。而你关心的是学习编程本身。你不需要那些复杂的东西。你想要的是一门为单个新手程序员设计、易于学习和记忆的语言。
  • “玩”起来。你更愿意用哪种方式学钢琴:是正常的、互动的方式,即每按下一个键就能立刻听到音符;还是“批处理”模式,即弹完整首曲子后才能听到声音?显然,互动模式让学钢琴更容易,学编程也是如此。坚持选择一门有互动模式的语言,并使用它。

基于这些标准,我推荐的第一门编程语言是Python或Scheme。另一个选择是JavaScript,并非因为它为初学者设计得多么完美,而是因为它有大量的在线教程,比如可汗学院的教程。但你的具体情况可能不同,还有其他不错的选择。如果你还不到十岁,你可能会更喜欢Alice、Squeak或Blockly(年龄大一点的学习者可能也会喜欢这些)。重要的是,你得做出选择,然后开始行动。

附录:书籍和其他资源

好几个人问我应该从哪些书籍和网页学习。我重申,“单靠书本知识是不够的”,但我可以推荐以下资源:

  • Scheme:《计算机程序的构造和解释》(Abelson & Sussman)可能是计算机科学最好的入门书,它确实把编程作为理解计算机科学的一种方式来教。你可以观看这本书的在线视频课程,以及完整的在线文本。这本书很有挑战性,可能会劝退一些人,但这些人用其他方法也许能成功。
  • Scheme:《如何设计程序》(Felleisenet al.)是关于如何以优雅和函数式的方式实际设计程序的最佳书籍之一。
  • Python:《Python编程:计算机科学入门》(Zelle)是一本使用Python的优秀入门书。
  • Python:Python.org上有几个在线教程。
  • Oz:《计算机编程的概念、技术和模型》(Van Roy & Haridi)被一些人视为Abelson & Sussman著作的当代继承者。它带你领略编程领域的宏大思想,涵盖的范围比Abelson & Sussman更广,同时可能更容易阅读和理解。它使用一种名为Oz的语言,虽然不广为人知,但可以作为学习其他语言的基础。

编者注

T. Capey指出,亚马逊上《问题解决大全》的页面现在“购买此商品的顾客也购买了”一栏下,出现了《21天自学孟加拉语》和《自学语法与文体》等书。我猜想,浏览那本书的人中,有很大一部分是从这篇文章过来的。感谢Ross Cohen在希波克拉底引文方面提供的帮助。