有很多软件工程师强烈反对我最后一句话所表达的观点。他们觉得,计算机科学之所以先行讲授,是因为需要解释代码的基本工作原理,而且,那些对基本概念缺乏理解的人将会编写出不合格的代码。然而现实情况则是,对于一名蹒跚学步的孩子,你是先教授语法和语言学理论呢?还是从简单的单词和句子入手呢?不学习单词并且使用它们,你很难理解语法。计算机代码就是由“编程语言”编写而成,由于编程语言与自然语言有许多相似之处,所以,学习它们的方式和方法应该也相似才对。我们应该就像学习一门新的自然语言那样,先学习编写代码 - 在开始介绍计算机科学“语法”之前,我们应该指导学生编写尽可能多的代码。
另外,优先讲授计算机科学(概念和理论)引入了一个不利于学习的步骤。传统的教学方式给学习者介绍了很多理论,并且只要求他们编写少量的软件。在他们能够完全掌握和理解他们自己编写这些代码以及如何应用这些理论之前,他们接触了过多的理论和概念。这种讲座、阅读和少量应用的学习方式很容易阻碍一名初学者。人们之所以想要学习编程,是因为他们发现有很多有趣的事情都可以通过软件来实现。他们也非常希望能做一些这样有趣的事情!就我个人的经历而言,在完成六个月的大学课程学习之前,我没有编写任何有趣的软件。对于一个人来说,在一件事情上不断努力却没有实质性的回报,这段时间太长了。很多人都不仅想说“编程对我而言太难了!”并且最终放弃。这就是我在那段时间里看到的实际景象。
那么,一项专为讲授编写代码而开设的课程计划应该是什么样子的呢?人们常说,当一个产品不再有新的功能特性需要添加时,这个产品并没有完成;只有当没有功能和特性需要删除时,这个产品才算完成。设计一门课程计划也是如此。在开始阶段,有哪些内容可以不讲?当学生接触变量这个最基本的编程概念时,老师倾向于立即引入一大堆数据类型。但是,为了更好地进行先行教授编写代码这一教学策略,我们应该将这一清单减少到一到两个数据类型。还有一些看似基本的概念,例如除法(对于计算机来说,这简直就是噩梦。),也应该推迟到绝对必要时再进行学习。试想一下,你试着给那些初学者解释为什么 2 除以 4 不等于 0.5,特别是当你输出这一计算结果,计算机实际显示为 0.5 的时候。实际上,你无需了解这一主题,就有大量的软件项目可供编写。通过学习一到两个布尔运算符(例如,比较两个数,看它们是否完全相等)而不是一大堆,你能够写出很多软件出来。关于数据是如何在内存中保存的话题,绝对值得再往后推一推。虽然对于复杂软件来说,这是必不可少的知识,但对于尚处在起步阶段的初学者而言,完全没有必要。
这项课程计划必须经过仔细考量和修订,所以指导老师需要为此付出更多努力。仅仅讲授少量的布尔运算符的一个明显的好处在于,指导老师无需跟踪哪个运算符在什么时间讲过这一细节。他们可以装作,尽管编写软件时只使用了一小部分运算符,但是经过一段时间的学习,学生们就会自然而然地理解其余部分。为了更好地为学生优化这一学习体验,更好地开展编写代码先行的教学策略,指导老师必须知道什么时间应该讲授什么内容这一具体细节,而不是敷衍了事。他们必须确保每一个作业都能用刚刚学过的概念和知识来加以完成。当然,这也需要更多的更有针对性的小作业。与其为所有的布尔运算符专门布置一项大作业,不如将其划分为若干个更小的单元更好。这一任务对于指导者来说极具挑战性,但这恰恰是为初学者设计正确步骤学习编写代码的核心工作。
表面看来,我似乎对计算机科学有点儿不太关心,然而事实不是这样。计算机科学对于构建复杂软件来说,不仅非常重要,而且绝对必要。但是,对于初学编程的人而言,却并非关键所在。他们缺乏理解计算机科学的应用场景。先行教授他们编写代码更加有趣,而且还给予了他们大量的成就感,使得学习进展更为顺利。最终,他们将到达他们所能编写软件的极限,这让他们开始发觉自己知识的空白。为此,他们想要填补这些空白。计算机科学正好可以帮助他们完成这项任务。实际上,先行教授编写代码向初学者生动地展现了为什么需要学习计算机科学的理由。
作者:Beekey Cheung,软件工程师,热爱经济学的同时,对教育也充满了激情。
感谢:Qingniu 帮助审阅并完成校对。
]]>很多人都想拥有更好的身材。为了实现这一目标,他们既可以通过节食,也可以借助锻炼。对于这两种情况,他们今后的任务相当简单。节食通常意味着计算卡路里(虽然有很多方法)并且长期坚持。锻炼通常意味着做一些心肺运动(走路,跑步,游泳,骑自行车等)并且定期进行。
即使是非常简单明确的目标,对于大多数人来说,都将非常困难。相对而言,观看电视、上网冲浪、小憩一下、玩电子游戏以及其它许多事情,反倒比节食或者锻炼容易许多。
相比之下,学习编程则要困难得多。在一定程度上,学习编程与学习外语极为相似。你也许可以学着说一门外语,尽管并不完美或者不完全正确,但是通常情况下,你都能侥幸过关(听众往往弥补了你的语法错误)。然而,你在编程上却不能如法炮制,因为语法精确性要求极其严格。任何一个局部的打字错误,都可能导致整个程序无法运行,而且很多时候,你还无法找到趁手的工具帮助你诊断和解决这一问题。
我们把这种情况与学习投篮比较一下。当你学习投篮时,你清楚地知道你的目标就是将球投进篮筐中。你投出的球一旦偏离目标,你马上就可以看到,目标与偏差常常清晰可见。对你而言,修正这一错误或许并非那么一目了然(你可能需要一些你还不了解的技巧),但是,即使是一名初学者,也能看出一个人是否射中了篮筐,然后告诉他这次投篮是否成功。
需要选择的东西太多
当你参与一项体育运动时,该项目规则和形式也许几十年都没有变化了。但是编程这件事,从另一个角度来看,则是一个持续移动的目标。新手甚至不知道,现在 Java 的版本为什么是 1.8(意思是 Java 语言的第八个版本)。
如果你在 1990 年代中期从事 Web 开发工作,你也许会用 Perl 语言进行 cgi-bin 脚本编程。如果你身处 1990 年代后期,你可能会采用 Java Servlet Pages 技术。如果你在 2000 年代中期学习 Web 开发,也许你会用到 Rails。如果你喜欢 Python,也许你会用到 Django。如果你五年前才开始接触编程,或许你正在摆弄 Angular、Backbone、Ember 或者 React。在这之前,也许还有 jQuery。
几乎每一天都有人想知道,他们究竟应该学习哪一门编程语言?他们担心错误的选择很可能将他们引向平庸。实际上,无所作为才是问题产生的真正根源。
配置就是一团乱麻
现在,Java IDE 的功能已经相当完备。方法提示、语法错误以及其它特性等。编辑器功能非常出色,代码调试也很不错。然而除此之外,其它一些事情却让我们极其痛苦。你的 IDE 难道可以告诉你,你的 pom.xml 文件是否正确设置?或者你的 Java 版本是否匹配?以及如何修复这些错误?大多数这类配置问题都会涉及烦人的手工调试。
如果你把文件放错了目录,没有任何信息提示你正确的位置应该在哪里。我认为 Spring 过度依赖注解简直就是一个糟糕的设计,因为它改变了 Java 原有的工作模式,致使那些学过 Java 编程的人突然无法理解,为什么一个注解应该放置在那个位置,以及这样做将会起到什么样的作用。
更普遍的情况是,所有围绕代码编写的事情都不友好
如果所有错误都能够被正确捕获和解释的话,我相信你一定会非常高兴。设想一下,你将 pom.xml 文件放在一个目录中,你怎么知道这个目录就是放置这一文件的正确所在呢?我在这个目录中没有找到 server.xml 文件,那么这个文件究竟应该在哪里呢?等等... 诸如此类的问题。
数据库真令人讨厌
数据库的工作方式已经存在很长一段时间了。就我个人而言,我宁愿将其看成一组 Java 对象,这样的话,我就可以批量处理它们。但是多年之后,我们还是会在 SQL 语句上遇到困难。所以,我们使用 ORM 试图简化这方面的工作,但是这样做似乎帮助不大,因为每一个数据库厂家更喜欢不同的做事方式。
不只是编程
一般情况下,你必须学会一些其它的技能,尤其是在 Web 开发领域。这些技能包括了 HTML、CSS、JavaScript、版本控制、错误跟踪、多种不同语言的编译及构建工具、各种基于不同浏览器的调试工具、数据库管理、服务器安全、电子邮件等等。这个意思就是说,仅仅学习和使用 Java(或者 Python)语言是远远不够的。
文档资料过时陈旧且难以使用
一段时间以来,Spring 几乎做什么都要用到 XML,现在反而转向了更传统的纯 Java 方式。那些基于 XML 的教程现在有什么变化吗?它们还在那里。难道不能用 Java 把它们重写一遍吗?不可能,这些资料只是一些简单的例子。截止到目前为止,我们搞不清楚为什么 Spring 当初选择一种有着无数选项的复杂设计。
或者,你也可以安装 Python 或者 Ruby。但是不管怎样,每过几年,编程界的一些做事方式就会发生变化。我应该使用 rvm 吗?或者某种类型的 gem?或者其它什么东西?
缺乏清晰的学习路径
大多数编程学习资源更喜欢于给你提供一系列的链接。当然,相对于设计一套教学或者学习大纲,这种资源制作方式更简单、更容易,但是,你也将因此遭受到各种教学法或者学习方式的折磨。
学生大都好高骛远
一般情况下,初学者都会被告知,他们应该自己构思一些项目。但实际上,他们什么也想不出来。然而,当有人要求他们做一些特定的项目时,他们又不感兴趣。
这看起来就好象是,你正在一次聚会的路上。你询问你的朋友们,他们喜欢在哪里就餐,他们说“任何地方都行”,然而,当你快速整理出一个清单之后,他们逐个否决了每一个选项。
学生们想要拖拖拽拽,而且他们喜欢视频教程,他们希望自己要做的项目应该是那种最复杂的 Web 应用,尽管事实上,Youtube 和 Facebook 上有数以百计的人比他们更有经验、更为聪明。
玩转刽子手猜字游戏的关键就在于选择合适的难度,因为后面还有很多更高难度的关口正在等着你们呢。
你需要掌握很多专业术语
是的,这是真的。在 C 语言中,static 表示什么意思?在 Java 语言中又是什么意思?overriding 与 overloading 的区别是什么?构造函数(constructor)与析构函数(destructor)的差异在哪里?什么是垃圾回收机制(garbage collection)?动态(dynamic)与静态(static)的不同之处是什么?一个 RESTful service 究竟是什么意思?
很难获得帮助
很多时候,这是因为初学者无法清晰地表达和描述他们的问题,或者人们觉得,有些初学者根本没有认真用 Google 查找。这就是为什么对于一些人来说,参加一门课程反倒是更好的选择,因为至少你可以与你的助教交流,或者与这门课的教授直接对话,或者询问其他同学。当你只身一人独自折腾的时候,你很容易遭遇困难并陷入窘境。
学习编程需要自律
最后,我觉得学习编程需要具备一种特殊的自律。在一定程度上,这种自律不同于健身或者节食,你应该在解决问题上练就一种更聪明的自律能力。如果你一旦陷入困境而不能自拔,你最好询问那些愿意给你提供帮助的人,或者,暂时将此问题搁置在一旁。
作者:CodeTinkerer
原文:Why learning to program is tough
感谢:Qingniu 帮助审阅并完成校对。
]]>关系代数 / 结构化查询语言
我感觉自己非常幸运。十四岁那年的夏天,我当时由于没有什么朋友,所以除了独自翻阅一本名为 MySQL and mSQL 的专业书籍之外,实在没有其它什么更好的事情可干。在这本书的评论里,尽管你可以看到这样的评价:『粗糙、不完整、几乎没有任何用处。』 然而事实上,我确实从中收获了结构化查询语言和数据库方面的知识。稍后不久,我又学习了关系代数(关系型数据库系统的基础理论),这成为我一生中最有价值的一项投资。我几乎无法统计出仅仅一条 LEFT OUTER JOIN 语句到底有多少次拯救我于水火之中。
就在我加入企业级软件技术公司之后,当我需要从 MySQL 转换到 Oracle 以及 MS SQL 的时候,关系代数的学习为我奠定了坚实的基础。在没有编程框架或者关系对象模型库的情况下,深入了解如何与数据库交互,帮助我快速地拓展了职业生涯。这就是为什么当我仅有二十岁时,我就能担负起一份为新墨西哥州圣达菲市搭建一个定制化网站的项目,而不是像其他同龄人那样,整天摆弄 Wordpress 和 Drupal 的各种功能插件。
如果你来自 Rails 阵营,或者经常使用其它那些原生支持数据库交互的编程框架,你能为你的职业生涯所做的最好的一件事情,就是学习关系理论以及结构化查询语言。阅读任何一本 C.J.Date 撰写的书都行。
Unix 进程模型
理解 Unix 进程帮助我真正搞懂了,当我运行一个计算机程序时,究竟发生了哪些事情。当然,它也帮助我深入理解了一个 Web 服务器到底是个什么东东,以及当我编写一个 Web 应用程序时,我实际是在做些什么。《高级 Linux 编程》有一个可供免费阅读的章节专门讲述了这一主题。实际上,这本书的所有内容全部开源免费。
当你还不了解进程这个概念的时候,编程对你来说就会变得更加困难、甚至更加神秘莫测。你将很难理解一个程序的性能表现,你将很难理解一个程序如何与其它程序交互。当你实际运行一个自己编写的程序时,如果你对将要发生的事情,有一种模模糊糊的感觉,学习 Unix 进程模型必将对你清除这些障碍大有益处。
正则表达式
是啊,是啊,我们全都听说过这个笑话:『总有一些人,当他们面对一个问题时,最直接的想法就是 ‘我应该使用正则表达式。’ 那么接下来,他们将不得不面对两个问题。』就我本人而言,我不太明白这段话的真正含义,因为正则表达式真 TMD 太牛逼了。我清晰地记着,十八岁的时候,我在一家酒店兼职担任夜间审计员,在晚上十一点到早上七点的时段里,当我翻阅完 O'Reilly 那本又厚又重的正则表达式教程书之后,我完全被它所拥有的强大功能给震撼住了。我们总在说,程序员特别擅长与文本打交道,但要是和正则表达式相比,根本不值一提。正则表达式是一个非常重要的工具,你通过这个学习资源就能够学会并掌握它们。
有限状态机
正则表达式就是在有限状态机的基础上构建的。这是一个关于有限状态机的优质教程,它给我们展示了一个正则表达式的具体实现过程。真是酷毙了!
我认为有限状态机应当属于计算机科学的基础理论范畴,但是由于我在大学只待了一年时间,而且即使在那一年里,我学习的东西全部是诞生于几千年前的作品,其年代远远早于计算机革命。直至大约六年前,我才开始实际接触这一专业理论。当时,我与同事正在开发一个移动应用程序。我们遭遇的问题是,我们必须以一种特定的顺序初始化这个程序,但是,确保正确实现的逻辑关系却因为相互纠缠而变成了一团乱麻。
尽管学习有限状态机占用了我们一些时间,但是在我们掌握了这个概念之后,描述这个程序的初始化过程一下子变得非常简单和清晰 - 只需表示成序列化状态和过渡就可以了。自那时起,我发现了绝大多数复杂用户界面代码都能采用这一方法加以改进和完善。就在几个月前,正当我使用 Hoplon 编程框架,准备从头开始设计实现一个在功能上类似 typeahead 实时提示特性的关头碰到了一个难题。但是,当我发现这个难题只需利用所有可能状态进行跟踪就可以很好解决的时候,我只用了几分钟时间就把这个问题搞定了,然后我重归正常工作状态。
情绪 & 情感管理
在个人生活中,我一直在学习和尝试情绪管理的各种方法和技巧。这主要是源于我渴望改善他人生活的愿望。另外,从自私的角度来说,学习它们能够帮助我更好地完成工作。情绪管理可能是我们每个人都亟待开发的一项最重要的元能力(meta-skill)。我的意思是说,情绪与情感正是我们人类的核心组成要素。
这本《非暴力沟通》是一份学习如何处理情绪问题的优质参考资料。另外,我的朋友 Alex Harms 近期刚刚编写完成了一部面向技术人员的专著,当然也同样值得一读。
这些就是我个人选出的最佳编程工具 - 我希望你们都能从中找到对自己真正有用的东西!
作者:Daniel Higginbotham,程序员,技术图书作者,Clojure 语言忠实拥趸。
感谢:Qingniu 帮助审阅并完成校对。
]]>颇具讽刺意味的是,恰好是这一点让很多人错误地以为他们喜欢编程。计算机的一般使用情景(点击、切换、刷新、打字、发送、砰砰砰)与计算机编程的现实情景(凝视、凝视、思考、挠头、编码、编译、失败、重复)完全不是一回事。很多人把编程误判为一类浅层工作,致使他们最终陷入了绝境。真正编程所要求的思维和行为模式与基于计算机的日常工作形态(浏览、发送邮件以及聊天)存在着巨大的差异,然而,他们却在期待以一种急于求成的浮躁心态来处理问题。编程需要耐心、置自己于思维极限的能力以及逐步向前推进。这些技能都可以习得,但是与学生们希望的学习方式或者他们已经习惯的行事风格,有着本质的不同。正是这个不同导致了转变思维和行为模式异常艰难。每个人都可以学会编程,但是企图凭借解决这个错误的问题,反而使学习编程变得困难重重。
我和很多新程序员打过交道。在大学的时候,有好几个学期我一直在教授一门编程导论的辅助课程。离开大学之后,我还在一家名叫 Mobile Makers 的技术培训机构担任过指导老师一职。我见过一些人学习编程就像他们早就知道应该如何编程一样,但是我也观察到另外一些人似乎撞到了一堵不可逾越的高墙上,而这堵高墙就是深度工作。这些人以为,他们正努力与各种编程概念纠缠不休,其实,他们是在和深度思考以及集中精力奋力争斗。当我自己在编程中遭遇麻烦时,大多数情况下,都是由于我无法将自己切换到深度工作所要求的思维状态上。
新近加入编程学习队伍的一些人经常问我,『我的数学功底不够好,我对此非常担心。』我想对他们说,不用担心数学。你真正应该担心的,是你是否具备一种慢慢、安静以及耐心工作的能力。只要你能做到这一点,其它的东西就会不请自来。
作者:Benedict Fritz,程序员 & 游戏开发者,生活在芝加哥。
原文:Learning Programming Isn't That Hard, Deep Work Is Hard
感谢:Qingniu 帮助审阅并完成校对。
]]>我的任务是为这个类增加一个新的功能。我认为这简直太『容易』了。『这个类就是我写的,因此,想出一个扩展它的办法应该毫无难度。』 就这样,在我享用完美味的午餐之后,便坐了下来开始着手编程。
刚开始一切进展顺利 - 对于新的功能如何融入这个类,我已有一个大致思路。然而,随着实现工作的越加具体和深入,我发觉自己原本模糊的思路越来越不完善。函数想要访问的数据根本无法获取。我早先设计的一个左右边界测试,也使得这个类变得相当脆弱且极易出错。当我在此基础上增添新的功能时,单元测试总是失败。
在接下来的几个小时里,我就像是掉进了一个深不可测的兔子洞里,甚至到最后,我几乎连自己编写的代码都无法辨识。我在本地代码与原始代码之间来回切换,试图找出它们之间的差异以及我究竟修改了哪些地方。在我的大脑中,这些代码的运行机制,或者说,我希望代码如何运行的基本心理模型早已不复存在。事情似乎已经堕落为我与计算机之间的战争。『赶快编译,该死的,编译!』
现在已是下午五点 - 仅剩一个小时就要回家了。我今天的工作只剩下这个亟待完成的功能。『我什么思路都没有,』 我这样想,『我只有一个小时的时间用来理清这一堆混乱不堪的代码。』
我沮丧绝望地从我的桌子旁边站了起来,头低垂着,朝着卫生间方向走去。我坐在马桶上,深深地吸了一口气 - 忽然之间,我的灵感来啦!
灵感的宝座
就在那一瞬间,我把所有事情都搞清楚了。代码飞快地掠过我的大脑,我可以看到这个类、它的全部功能及其所有用例。我已经清楚地知道新功能的代码应该放在何处。对我来说,所有这些再清晰不过了!
完事后(回到座位前,不要忘记洗手!),我迅速回到自己的座位上开始敲击代码。我的手指已经不能跟上代码闪现在我大脑中的速度。键盘在我手指的强力敲击之下,开始出现结构性松动。我和我的计算机已经不再互为敌人 - 我们是亲密的伙伴,我们正朝着一个共同目标一起努力。
三十分钟后,代码成功完成了编译。单元测试全部顺利通过。我把这项新功能的需求特性逐一运行了一遍,每一项都符合要求。『我完成了这个不可能完成的任务,我已经搞定了!』
当我从编程高潮回到正常状态时,我有了一个唯一必然的结论:我经历过的最高效的编程体验不是发生在键盘前,而是在马桶上。
回顾与反思
现在我不打算说,马桶真有架构代码的神奇魔力(尽管我的确认为它就是一项伟大的发明)。可是,我想说的是,如果你和你的计算机稍微分开一会儿,并且从一个更高的层次开始思考,绝大多数琐碎的编程任务将会变得超乎寻常的容易。无论是去趟卫生间,还是在公园里散步,或者只是在办公室的厨房里稍事休息,几乎任何一项远离计算机的活动,都会让你的大脑重新焕发活力,使你再次重见森林。
许多程序员就是不愿意离开他们的桌子。他们觉得,除 IDE 之外的任何时间花费都是浪费,或者担心招致其他人的轻视和误解。他们的管理者们可能会说,『他为什么不坐下来工作呢?难道想要被降级。』
依我看,这种逻辑不仅完全颠倒,而且适得其反。给程序员支付薪水,不是为了让他们坐在一张桌子旁边,目不转睛地盯着屏幕,或者不停地编写代码。程序员的真正目标是给最终用户创建功能,而通向这一目标有很多途径。如果离开你的桌子,能够让你的功能创建活动变得更加快速有效,那么,这就是你应该做的事情。
总而言之,当你编程的时候,不要忘了使用卫生间。
作者:Brian.S.Lam,视频游戏爱好者、软件工程师 & 超低产博客作者。
感谢: Qingniu 帮助审阅并完成校对。
]]>我永远不会忘记我们刚在一起时的第一个函数:
$(document).ready(function(){
alert(‘page loaded’);
});
哈哈!我希望你原谅这个看似有点儿鲁莽的消息提示框。我过去常常做这样的事,我希望确认你是否处于正常状态。毫无疑问,你已经可以工作了,我对此毫不怀疑。近一段时间以来,我们已经很少做 $(document).ready() 这类事情了,但是,我依然怀念我们过去的那些美好时光。当然,我还能清晰地记得,在没有你的岁月里,我曾经遭受过的那些痛苦!
每当事情变得异常艰难时,你总是陪伴在我身旁。正是由于你的到来,我的生活开始变得一致、整洁而且有序。有时候,我对此甚至都没能察觉。万维网是一个混乱的地方,是你带来了秩序,是你赋予了我自信。
每当我不知所措时,你总是在那里。你帮助我完成了很多我从没有单独完成的工作。在一定程度上,你甚至让一些事情变得简单过了头,以至于我竟然做了一些我本不该做的事情。为此我非常抱歉,我想这是我的错,与你无关。
我可能有点儿肤浅,但是我就是喜欢你的样子。我随处都能认出你的表单。我尤其喜爱你干净整洁的闭包和链式方法,你简直让我欲罢不能。你总能给我一种舒服熟悉的感觉,你总能让我面露阳光灿烂的笑容。
你是那样的忘我无私。事实上,你是那样的无私 - 你从不让我过度依赖你。你总在教授我应该如何思考。而且不仅是我,我周围的世界都已被你感染和影响。每一次我听到有人说『原生 JavaScript』,我就会笑出声,而且,我就会马上想到你。你是那样的光芒四射,人们甚至要用一个术语才能准确描述你的缺席。你已经成为了我的精神领袖和指路明灯。我想,这就是我为什么爱你的原因。
我希望那些不熟悉你的的人,能够给予你更多的尊重。就像那些 Angular 和 React 的年轻追求者一样,他们总是来也匆匆,去也匆匆。在他们的心里,你或许还是会给他们留下一点儿痕迹,以便他们在将来做出对比。但是不管怎样,你永远是我的初恋,你永远是我的真爱。
每次我听到有人说『我不需要 jQuery』之类的话时,我就会感到非常伤心。他们似乎已经忘记,或者根本就不知道,在你到来之前,这个世界是多么黑暗。我们过去需要你,我们现在依然需要你。我喜欢你做事的方式,尽管很多年已经过去,对于有些任务来说,与其它解决方案相比,你仍然是那么出色。我信任你。我了解你,当然你也了解我。在这个世界上,新的做事方法总是会不停地出现,但是我知道,你永远值得信赖,而且,当我需要你的时候,你就在那里。
所以我想说,谢谢你 jQuery!这真是神奇美妙的十年。我希望我们还能有下一个十年。即使这一愿望无法达成,在我的记忆中,我对你的敬重之情将会永存,你之所以消失,就是因为你做事情的方式太过于完美。如果告别时刻终将来临,那也是因为你已经奉献了你的全部。不再需要并不意味着,你对我和万维网来讲已经永远不再重要了。
谢谢你 jQuery。
作者:Mike Riethmuller,Web 开发者 & 前端工程师,生活在澳大利亚。
感谢: Qingniu 帮助审阅并完成校对。
]]>所有环节都出现了问题,简直就是完全的彻底的混乱不堪。在日常工作中,我们总没有足够的时间用来修复这些愚蠢的错误,而且,绝大多数的公司和个人,对于已知的如何创造出更好软件的科学与知识甚至毫不关心。
即使是在那些看似能够按照正确的做事方式,创建真正有用且易于使用的正确软件的环境中,除去重复特定的工作流程之外,你几乎什么也做不了。
最终你将会发现,你构建的东西还是糟糕极了,你所做的一切只是为你所痛恨的混乱状况平添了更多麻烦而已。
上述部分就是当我陷入窘境的时候,我大脑呈现出来的直接反应。
实际上,我成功编写过很多代码,它们顺利通过了形形色色的测试,为此,我的感觉依然很好。我当然喜欢编程以及解决问题,非常喜欢!
但是有的时候,我就是想一把火烧掉我编写的每一行代码。
我由衷地想对所有渴望创建一些有价值东西的人说一句:千万不要气馁。可是,正如我们大家都倍加认同的那样,这可真不是一件容易的事呀。
作者:Felix Geisendörfer,程序员,工作中经常使用 Go、JavaScript、HTML 以及 UNIX 工具。
原文:The worst part about programming …
感谢: Qingniu 帮助审阅及完成校对。
]]>生产力
我曾经见过一些人,他们的工作效率远在我之上。可是我想说的却是,个人生产力的高低与团队效率完全无关。
通常情况下,生产力是按照产出结果的数量来定义的。
『真正有效的生产力,特别是在工业界,其计算方式是每单位投入与产出之比。』
规模不经济
然而,我们面对的问题在于,软件在一定程度上具有规模不经济的特点。我们想要构建的代码越多,构建和维护软件的成本也就愈加昂贵。随着软件规模不断增长,我们将会花费更多的时间和金钱在:
也就是说,单个人员的生产力越高,团队运行的整体效率可能越低。
我们的工作真的有效吗?
在我们所做的所有事情之中,最终来看,只有很少一部分能够创造出足以证明我们自身价值的东西 - 伴随着开发工作的推进,我们才会逐步有的放矢地专注在这些高价值的事情上。
如果我们实现了一个深受用户喜爱的功能,这就让判断我们工作的有效性变得非常容易。如果这项特性带给我们的收益远高于成本的话,那就更容易判断其价值了。
当你将这些工作成果与那些团队长期以来一直付诸于努力的工作方向进行比较的时候,你的感觉又是怎样的呢?我们所做的每一件事情都有其机会成本,因为你一旦选择了一个方向,就不能在另外一些地方,或者说更有价值的工作上付诸努力了。
0.1 倍速应用原则
有时候,关注不做什么反而更能让我们团队的工作有效性大幅提升:
价值识别其实很难
鉴于维护每一件我们所构建的东西都有其固定成本,我们不如只在 10% 的工作上付诸努力,如果我们可以想出真正有价值的 10% 的工作究竟是什么的话。
至于这 10% 的工作,我们甚至情愿花费 10 倍以上的精力将其持续维护成本减少到最低。
现实中场景中,探寻哪些工作最具价值,以及哪些工作纯粹是在浪费时间,其实才是真正的挑战。
作者:Ben Jiweber,软件工程师,Unruly Tech 技术 leader。
原文:Why I Strive to be a 0.1x Engineer
感谢:Qingniu 帮助审阅并完成校对。
]]>为此,他的解释是这样的,作为一名技术顾问,他可以通过支持这些很难使用的技术获得良好的商业回报。我的这位朋友绝对诚实可信,他不会随意向客户推荐他认为不合时宜的技术方案。所以,我觉得在他与客户实际交流时,他的真实想法有可能更接近这样一种情景:『如果我们完全从头开始,我不会建议你采用这样一技术。但是你们已在这项技术上投入了许多,我希望利用我在这种技术上的特长帮助你们,或者,协助你们将此项技术迁移到其他技术之上。』
有时候,这种谋生手段看起来并不令人愉悦,而且似乎也面临着不小的风险。如果有些事情真的没有必要搞得那么复杂的话,更好的替代解决方案很容易显露出来,或者突然地出现。(假设我们具备自由选择权,某些决策不会被法律禁止。)
在理由正当的情况下,学习复杂技术是一种明智之举。抽象层面较低的工作总是更难一些,但是不管怎样,始终需要有人去解决那些其他人不曾关注的问题,而且,由于这类工作很多人根本做不了,因此,有人借此获得更高的收益,或者得到更多工作机会和保障,自然无可厚非。
然而,选择复杂或者难度较高的技术,在一定程度上也存在着一些危险之处。一个就是滥用这项技术的诱惑,即使是在不需要的情况下。另一个就是,那些真正需要复杂技术解决的问题,也许会随着时间流失而变得越来越少。
作者:John Cook,应用数学博士,技术咨询顾问。
原文:Learning (needlessly) hard technology
感谢:Qingniu 帮助审阅并完成校对。
]]>“你们觉得这是什么?” 国王问。
其中一名顾问,也是一位工程师首先回答道:“它是一台烤箱。”
国王问:“如果要为这台烤箱配备一套嵌入式计算机系统,你的具体设计是什么?”
工程师回答说:“我将使用一个四位的微控制器,然后编写一个简单的程序,读取火候调节按钮的当前位置信息。我把火候信息量化 - 从雪白到漆黑,总共 16 焦度级别。预置的烧烤时间存放在一个仅有 16 行的以焦度级别为索引的数据表中。然后,启动加热器,读取预置烧烤时间,计时结束后,关闭加热器,弹出面包片。给我大约一周时间,我就可以拿出这个系统的原型。”
第二名顾问是一位计算机科学家。他很快意识到了工程师缺乏远见想法的危害性。他说:“电烤箱不只是用来烤面包片的,它还可以加热冷冻鸡蛋饼。我们之前看到的东西,实际上是一台早餐加工机。我们国民的生活异常丰富,他们需要多功能的机器,比如,一台能烤香肠、煎培根、炒鸡蛋的早餐加工机。功能单一的烤面包片烤箱很快就会过时的,如果现在考虑不周,两到三年后,我们就不得不重新设计这个烤箱。
鉴于这一事实,我们应该设计一个更加聪明的解决方案。首先,创建一个早餐食品类。然后,从这个类派生出一组子类:面食类、肉类、禽蛋类等等。面食类进一步派生出面包类、松糕类、煎饼类、蛋饼类;肉类派生出香肠类、肉串类、培根类;禽蛋类派生出炒鸡蛋类、水煮蛋类、荷包蛋类、煎蛋类以及各式各样的蛋卷类。
对于干酪火腿煎蛋卷这个类,我们需要一些特殊处理。它必须同时继承肉类、乳制品类和禽蛋类的特性。因此,缺乏多重继承机制无法解决这个问题。程序运行起来之后,它必须可以正确地创建对象实例,然后向对象发送『开始加工』的消息。该消息触发何种操作要取决于对象的类型,这样的话,同一条消息就可以激活从烤面包片到炒鸡蛋的各种不同操作啦。
基于以上所述,我们在分析阶段,必须将核心需求界定为加工不同种类的早餐食品。在设计阶段,我们还要进一步明确由此衍生的其它需求,比如,我们必须采用一种拥有多重继承功能的面向对象语言。另外,诸如鸡蛋已经晾凉,但培根还没有烤好的情景完全不能接受,所以,多任务并发处理的功能和机制也是必要的。
千万别忘了用户界面。控制杆不适于加工多种食品,火候控制按钮也容易让用户摸不知所措。用户更喜欢购买那些具备友好的图形化界面的产品。
当早餐加工机通电之后,用户应该在屏幕上看到一名牛仔。点击这名牛仔之后,屏幕上就会显示『启动 UNIX v.8.3』的信息(UNIX v.8.3 版本将在早餐加工机上市前发布)。系统完成启动之后,用户就可以打开下拉菜单,在菜单中点选他们想要加工的食品。
在设计阶段,我们首先详细定义了软件的功能特性,接下来我们要为具体实现选择一种适当的硬件平台。带有 8 兆内存、30 兆硬盘以及 VGA 显示器的英特尔 80386 机型应该足够用了。如果你选择一种支持多任务、多重继承、以及内置图形化用户界面开发包的面向对象语言的话,那么当你编写程序时就会轻松许多。(与之相比,那种首先确定硬件环境,然后再把自己禁锢在四位微控制器上的思路是多么愚蠢啊!)
国王把这位计算机科学家扔进了护城河里。自那之后,国王和他的臣民们一直过着快乐的生活。
参考:国王和电烤箱
感谢:Qingniu 帮助审阅及完成校对。
]]>当然,他们对此有不同的想法。
我觉得,如果你仅仅了解 jQuery,你最好申请一个前端工程师的岗位。你难道认为『软件工程师』就只是意味着 HTML、JavaScript 和 CSS 技能吗?
(我喜欢那些人 - 他们一旦谈起 XML、JSON、XSLT、SOAP、HTTP、REST、SSL 等首字母缩略语总是滔滔不绝,如果他们还能区分整数与浮点数据类型的话,那就更好了。)
你真的可以编程吗?
对于软件工程师这个岗位,我的意思是指,你能够用代码编写出一些东西。我在这里谈论的是真正的编程:我给你一个问题,你可以使用任何你喜爱的编程语言构建出一个相应的解决方案。
你能够完成这项任务吗?
好吧,我们来做个约定:如果你无法在一个小时之内分别解决以下五个问题,你或许应该重新审视一下你的简历。在你目前的工作岗位上,你很可能干得相当地不错,但是你(暂时)还不能称自己为『软件工程师』(或者『程序员』或者『计算机专家』或者『软件开发者』。)你或许应该坦白地面对自己,并且情愿花费一些儿时间再一次加固一下自己的专长和技能。

五个问题
(以下问题表面看起来都非常简单,但是你很快就会发现,这些问题实际上还挺有难度,一开始你恐怕对此毫无头绪。我没有开玩笑噢!)
问题一
编写三个函数,分别使用一个 for 循环,一个 while 循环,以及一个递归,计算一个数值列表中的数值总和。
问题二
编写一个函数,通过交叉接受列表中的元素合并这两个列表。例如:两个列表 [a, b, c] 和 [1, 2, 3],函数应该返回列表 [a, 1, b, 2, c, 3]。
问题三
编写一个函数,计算斐波那契数列的前一百位。根据定义,斐波那契数列的前两位数分别为 0 和 1,每个序列数是前两个数的和。例如,斐波那契数列的前十位是: 0, 1, 1, 2, 3, 5, 8, 13, 21, 以及 34。
问题四
编写一个函数,接受一个非负整数列表,并将其重新组合为一个最大的数。例如,一个数值列表 [50, 2, 1, 9],其可组合的最大数为 95021。
问题五
编写一个程序,输出符合以下条件的所有可能的数值组合:接受一个 1, 2, ..., 9 数列,然后利用加、减或者无操作使其结果总是等于 100。例如:1 + 2 + 34 – 5 + 67 – 8 + 9 = 100。
作者:Santiago L. Valdarrama,自 1994 年开始编写软件,现在 Levatas 就职。
原文:Five programming problems every Software Engineer should be able to solve in less than 1 hour
感谢:Qingniu 帮助审阅并完成校对。
]]>我一直在思考如何将这一理论引入到编程领域。编程是一种智力挑战型任务,但是幸好我们发明了各类工具,从而使得这项任务具有了一定的可管理性。我发现任何一门编程语言、开发框架,或者编程库的直觉性和易用性,可能均有其相应的副作用。从我的个人经历以及帮助初学者的感受来看,我注意到一种现象,当我们使用一种符合我们直觉的工具时,任何时候只要遭遇到困难,我们就会觉得手足无措。而且,尽管我们或许已经具备了克服困难的必要技能,我们还是会尽力寻求帮助,并且重新回顾我们的已有工作。我们宁愿直接找寻与开发框架相关的最佳实践,也不愿意自己动手解决问题。在这一方面,最典型的例子就是那些 Stack Overflow 上的问题,如『如何使用 jQuery 实现 X 功能?』 或者类似的回复,如 『使用 jQuery [插件] 就可以实现 X 功能』,在这里,X 泛指从基本算法到 websockets 之类的任何对象。
开发框架负向空间
我们在使用一种开发框架时,一些特定问题将会变得很容易解决。如果我们一直待在这个开发框架的应用范围之内,编程显得非常直观。我们把这个应用范围称为『开发框架直觉空间』。另外一方面,我们把其它那些这个开发框架无法解决的问题范围称为『开发框架负向空间』。负向空间不是一个开发框架的缺点,它原本就不属于这个框架的应用范围。但是,如果一名程序员在直觉空间滞留的时间过长,那么,当他一旦进入负向空间,就会感觉不舒服或者特别尴尬。
当那些初级程序员发现自己身处负向空间的时候,他们常常期待编程库的作者能够让他们重回直觉空间。这就是为什么任何一种流行的开发框架,都有着完整且自成一体的生态系统 - 这些插件及扩展库帮助这个开发框架,在表面上扩大了直觉空间的覆盖面。如果程序员的生产效率借此得以大幅度提升,似乎也无可厚非。但是,它很可能导致一系列无法预料的后果:
开发者与编程库作者的代码依赖
我其实在开始的时候就应该说,从技术上来讲,这是一种错误的二分法。在任何编程过程中,所有的程序员都承担着这两种角色。你也许正在为产品逻辑编写代码,然后你转而构建一个可以帮助你多处复用的具有通用价值的抽象模型。但是,在开源软件世界,我发现人们更倾向采用这种二分法策略。
在开源世界里,通向成功的最简单办法,就是自己动手将负向框架空间转换为直觉框架空间。也就是说,编写各种插件和扩展库。当一个开发框架日渐流行的时候,越来越多的的开发者(通常都是一些初学者)将会开始抱怨:在这个框架中实现 X 功能如何如何困难(其实我们都知道,X 功能与这个框架的设计初衷根本没有关系)。就像在真实商业世界一样,开源软件之间竞争异常激烈,当你开始为大家解决一个已知问题的时候,很多人也就有了更多无需进入负向空间的机会和可能。这将会大大强化『程序员理应将所有时间花在直觉空间上』这一错误观念。
小结
我认为,修复这一问题的根本解决办法,就是要回归教育。当一个人在最早期学习编程的时候,我们的文化过分强调了工具的重要性。我自己就收到过很多这方面的问题,如『有哪些最好的工具或者编程语言值得我学习呢?』 在我看来,这些问题提出的时间过早。我过去经常这样回复:『这取决于你正在构建的究竟是什么』或者『选择一个对初学者友好的社区』或者『投资在那些成长型的编程语言上』。尽管我觉得所有这些都是很好的答案,但是对于一名编程初学者而言,所有这些并没有看起来那么重要。当你正在学习如何编程的时候,核心内容并无差异。更有甚之,这种风格的回答还容易导致过于沉迷工具的习惯。
对于软件工程来说,代码重用、编程库、分享以及开源软件都非常重要。但是我们应该小心仔细地处理这些事务 - 不要建立错误的观念,以为编程本身就像把不同的对象粘接在一起那么简单。事实上,在过去的一段时间里,我总是对那些看似简单的编程活动保持高度警惕。如果编程真的那么容易的话,这一领域早就自动化了。
作者:Amjad Masad,程序员,就职于 Facebook JavaScript 基础架构团队。
原文:Overcoming Intuition in Programming
感谢:Qingniu 帮助审阅并完成校对。
]]>在整个的阅读过程中,有一件事情触发了我的思考。John Sonmez 在书中说,他所见过的每一位著名或者超级成功的人士,都极力强调了阅读的重要性(在这本书的结尾,John Sonmez 也为我们特意编制了一份阅读书目)。
显而易见的是,如果这些成功人士确有很多书籍向我们推荐的话,他们一定读过很多很多的书!为什么这些成功人士如此热衷于读书呢?也许在我们这个领域,甚至所有的领域,阅读和成功之间一直有着某种特殊的联系。
我相信在这两者之间,一定存在着一种相互作用的关系。每一位渴望成功的软件开发者都应该留出一定时间,用于阅读软件工程类书籍。我不打算告诉你究竟有哪些图书值得一读,因为坦白地说,我认为这一点并不重要。
你想要寻找一些你感兴趣的图书,或许可以从这份软件工程师最佳图书清单开始入手(我个人更偏爱电子版)。
不要企图一鼓作气地读完它们。如果你真要这样做,很快你就会精疲力尽,而且还会产生厌倦感。你完全可以安排一个略显轻松的阅读节奏,比如两周为一个周期,因为只有这样,你才会有足够的时间,用来消化那些暂时滞留在你大脑中的知识和信息。
即使你阅读的这些书籍与你的日常工作不直接相关,你也可以从中发现对你工作有价值的一些重要参考和启发。当你由于距离问题过近而无法发现一个解决方案的时候,你或许可以借此获得不同的视角和看法。
当然,读书的理由还有很多,比如,学习新的技术,或者扩展你的技能组合。但是我认为,除去这个重要原因之外,其他的收获都只是额外的奖励而已。
好了,这就是我为什么认为阅读对于所有软件工程师非常重要的原因!
作者:Isaac Jordan,软件开发者 & 格拉斯哥大学计算机专业研究生。
原文:The Importance Of Reading As A Software Engineer
感谢:Qingniu 帮助审阅并完成校对。
]]>现在,我不再采用这种方式回答这个问题了。相反,我已经有了一个放之四海而皆准的标准答案。
就在我们将要进入 2016 年的时候,Web 开发领域的变化步伐日渐加速这一现象已经愈加明显了。本来就没有可用于学习的『唯一编程框架』这种事情。事实上,这个世界没有任何一种东西是一劳永逸的。仅仅在 2015 年一年的时间里,我已经使用过三种不同的构建工具、三种不同的 Web 开发框架,以及三种不同的编程语言。
我所经历的唯一不变的事情就是持续学习。
疯狂的技术正在绵绵不断地涌现出来,尤其是在 Compile-to-JavasScript 领域,发展速度远比以前快了许多,从长远来看,我不认为未来的技术一定会建构在我们当前所拥有的编程框架之上。即使是 React,它也只是我的选择方案之一(求求你,千万别问我的选择方案具体有多少)。
如果你想要你的技能永葆青春与活力,只需确保在一件事情上投入即可:你的学习能力。
那么,你应该怎样做才能学会学习呢?
鉴于我们正在谈论的话题主要涉及前端 Web 开发,我建议你可以从学习 JavaScript 语言基础开始入手。尽可能多学一些,学得越多越好。找到那些对你真正有用的方法(图书、博客、视频、练习等),深入地钻研下去。请记住,这是一种终生学习。当然,我建议你至少应该花费一个月时间专注在这一方面。你在这一段时间学到的知识和技能,将会成为你未来成功的基石。我这样讲的意思并不是说,你必须辞掉你目前的工作,或者忽略你正常的社交生活。我的意思是,在你已经分配给这项学习的时间里,你应该百分之百地专注在这项主题上。如果你已经是一位 JavaScript 『大师』,这个时间也许一个月都用不了,但是一定要利用这个时间复习一下,因为有时候,你很可能对自己的知识缺陷并不了解。
在此之后,你就可以进入 Web 前端技术堆栈的下一个抽象层次:文档对象模型(DOM)。重复上述我们讨论过的学习策略,直至你熟练掌握这一主题。
当你在这一领域建立了较为深厚的基础之后,相比之前而言,学习编程框架将会变得异常轻松自然。在这个阶段,我推荐你学习尽可能多的编程开发框架。我之所以这样说,并不是意味着,你必须将这些东西都用于生产环境之中。但是你应该构建尽可能多的简单的应用程序。你将会发现,这种一个框架接一个框架的学习方式非常高效。不久之后,你就能在几个小时之内掌握一个编程框架的精髓,而不是数天之后。
这种学习方式的目的,不是迫使你精通每一种编程框架,而是为了让你更加擅长学习新的技术和新的工具。我不是说你不应该精通一种编程框架,或许,你应该选择一到两种编程框架,把它们重点应用到你目前的工作上,或者,你想象中的某些未来场景之中(直到其淘汰,最终一定会出现这种情况)。这项练习的宗旨主要是为了磨砺你的学习能力,并且使你永远保持一种学习状态。
大多数人无法在他们的工作或学习中开展和实施这项任务。因此,它可能会占用一部分你工作之外的时间。终有一天,我们这个行业能够进化到支持这种类型学习的那种阶段,因为这会给我们所有的人带来好处和利益。但是截至今天为止,大多数公司还不是这样。因此,你必须利用自己的时间来完成这项任务。就像你喜欢经常浇灌自己的花园一样,我建议你找寻一个自己特别感兴趣的业余项目,当你需要学习新的工具和框架的时候,你可以周而复始地重用这个项目。
如果你做的还不赖,你不仅会成为一名优秀的 Web 开发者,而且你还会大幅改进你在一个全新领域快速攀爬以及提升自我的能力。希望进入移动开发领域吗?没问题。希望进入后端技术开发吗?没问题。希望进入嵌入式开发领域吗?也没问题。你非常擅长学习和掌握新的知识与技能。
总之,把你的主要精力投资在学习能力上,要比你学习任何特定的语言、技术或者工具都要重要。努力避开那些过度炒作的技术概念,通过持续学习学会如何学习才是真谛。
作者:Dave Smith,软件工程师,就职于 HireVue 公司。
原文:What web framework should I learn?
感谢:Qingniu 帮助审阅并完成校对。
]]>从很多方面来看,SICP 都是一次革命。其中最为重要的是,它大幅提升了计算机科学专业入门课程的内容门槛。在 SICP 之前,计算机科学的第一门课程里几乎总是充斥着与学习编程语言相关的内容细节。SICP 完全与之相反,它更多地站在全局角度思考编程的整体过程,远离编程语言的具体细节。它将注意力重点放在了抽象的核心思想之上 - 从特定问题直至构建软件工具,帮助你探求一般性的规律。SICP 大量使用了『函数即数据』(functions as data)在这一思想 - 一种在初始阶段难以理解和学习,但是一旦掌握之后便会得心应手的编程模式。(在初等微积分教学中,采用了同样的教育模式,即在开始阶段,哪怕你的数学功底异常扎实。学习过程也会比较艰难。)就在其它许多主题相似的课程还未谈到一个编程范式的时候,SICP 已将三种不同的编程范式(函数式编程、面向对象式编程以及声明式编程)巧妙地融入了一门计算机科学的入门课程之中。

SICP 的另一个创新之处就在于,它选择了 Scheme 作为教学语言。那个时候,绝大多数的计算机科学入门课程都在使用当时的热门编程语言:从 Pascal 到 C、C++、Java 以及 Python。Scheme 在工业界从没有大范围使用过,但是,对于计算机科学导论而言,它的确是一个完美的选择。一方面,Scheme 有一套可用来表示任何对象的非常简单统一的符号系统。一般情况下,其它编程语言会用一种符号表示变量赋值,另一种符号表示条件执行,两到三种符号表示循环,还有其它的符号用来表示函数调用。很多讲授这些编程语言的课程至少会花费一半的时间用于学习这些符号。在伯克利的 SICP 课程中,我们大约只需一个小时的时间就可以把所需的这些符号全部学完。在这一学期的剩余时间里,我们集中精力学习计算机科学核心思想和方法,而不是编程语言的语法。当然,尽管 Scheme 语法非常简洁,其功能却是多才多艺。Scheme 不仅可以帮助我们体验三种不同的编程范式,而且,更为特别的是,它还让能让我们查看面向对象的编程究竟是如何实现的。正是因为这个原因,对我的学生来说,那些 OOP 语言既不神奇也不陌生。由于 Scheme 是 Lisp 语言的一个分支,所以它能够游刃有余地将函数作为数据对待。与那些常见的专业编程语言相比,Scheme 更像是一个剥离了华丽外衣的原生编程工具。Abelson 和 Sussman 教授在他们的计算机导论课程中采用 Scheme 几乎就是一项勇敢的壮举。他们认为,一旦你了解和掌握了这一思想,当然,这也是我的个人亲身经历,学习其它编程语言将不再是一件难事,充其量也就是一个周末的付出而已。我告诉我的学生,『那些你在工作中将会经常用到的语言或许还没有出现呢,所以我们恐怕无法给你们讲解它们。与之相反,在这些语言出现之前,我们必须教会你们如何学习一门新的编程语言。』
最后,SICP 在看待大学新生从这门课程中取得的收获上始终保持积极乐观的态度。通常情况下,设计构建编译器可能更适合经验丰富的高年级学生,但是在 SICP 课程中,这项要求是必须的。SICP 这本教材的文字并不便于阅读 - 缺乏现代课本具有的排版风格 - 为了帮助学生在短注意力周期的条件下,保持他们兴趣而特意设置的侧边栏、彩色文本框以及照片等。没有冗余的课后练习,每一项练习讲授一个重要的新思想,每一段话都非常重要。SICP 喜欢使用一些『大』概念,但是在你仔细阅读之后,总能得到回报。
统计显示,SICP 风格的课程一直以来都属于少数派。但是这本书的影响力已经远远超过了一般范围。SICP 激励了很多后续的作者,他们非常渴望能够达到 SICP 的水准。采用 Scheme 作为教学语言已经扩展到了从中学到研究生院范围。即使在一些主流的计算机课程中,尽管更多侧重面向对象的编程,但是也开始越来越重视编程范式。计算机科学这门专业应该更多地关注思想、应用环境以及计算的社会影响,而不是完全依赖编程实践或技巧。
SICP 作为一本入门级计算机科学教材,其资历已经非常深厚了。迄今为止,SICP 已经出版超过了25年,目前来看,还没有停止印刷的打算。计算科学一直在不断地向前发展,从大型主机到个人电脑,直至移动互联网的出现。但是这些深藏在变化之中的那些基本思想始终未变,它们早已完整地包含在了 SICP 中。
我从 1987 年开始教授基于 SICP 的课程。这门课程随着时间的变化也在不停的修订。我已经为其添加了有关平行计算、并发控制、用户界面设计以及客户端/服务器技术等新内容。但是总体结构和内容没有任何变化。大约每过五年时间,我们的一些老师就会提出建议,这门课程采用的编程语言应该使用 X 语言替代。每次遇到这种情况,我就说,『如果有谁能用这门语言写出世界上最好的编程教材,我们再做决定不迟。』 而且,迄今为止,我们每一次的投票结果显示,SICP 课程应该保持原样。你们很快就会知道,这门课程是否会在我退休之后继续存活下来。
备注: 伯克利最新的计算机科学专业入门课程采用的编程语言是 Python。包括课程讲义在内,其内容与形式依然遵循和保留了 SICP 的主要特色。)
近一段时间,麻省理工学院由于重新设计了计算机科学与电子电气工程系的低年级系列课程,致使这一讨论变得异常尖锐。一些来自麻省理工学院之外的人将这次对课程的重新设计描述为『麻省理工决定切换至 Python,』 但是这不符合实情。麻省理工的真实决定是,从一种以主题为中心的课程设置模式(编程范式,然后是电路,然后是信号处理,然后是架构设计)转向另一种以应用为中心的课程设置模式(让我们一起来构建一个机器人,让我们一起来构建一个移动电话)。他们课程中的每一项内容都需要重新组织。编程语言的选择只是其中最小一项。这种新的方式给教学工作带来了很多困难和挑战。举一个例子:每一门课程都要求两个专业(计算机科学与电子电气工程)的老师紧密合作。或许一段时间之后,这种应用导向的教学模式将会引发一场就像 SICP 一样影响深刻的革命,但是目前看来,这场革命还没有发生。
在我的个人经历中,很少有学生在他们还在学习 SICP 这门课程的时候,表达他们对这门课程感谢之词。但是在一项针对所有计算机系学生的调查中,SICP 却是最受欢迎的课程之一,而且,经常有不少毕业许久的学生来拜访我,或者给我发来电子邮件,他们告诉我说,这些当他们还是学生时看似毫无用处的『象牙塔』概念,正在被实际应用到他们的具体项目之中。这让我们似乎觉得,谷歌公司基于函数式编程思想的数据并行处理技术 MapReduce 的发明,已经协助我们消除了『象牙塔』这一名声。
作者:Brian Harvey,计算机科学教育创新者,退休前在加州大学伯克利分校专职从事教学工作。
原文:Why Structure and Interpretation of Computer Programs matters
感谢: Qingniu 帮助审阅并完成校对。
]]>