来源:可译网
译者:ericzc
在我还是个新人的时候,我碰见了个同学,他宣称自己可以用任意我能说出名字的编程语言编程。吃惊之余,我满怀不信的问道:“如果是用那些晦涩难懂的语言,一大堆指令仅仅模拟了一个简单图灵机你也行么?”,他淡定的回答道:”啊,那种语言被叫做烧脑语言,我知道烧脑语言。“
这下我被烧脑了,他没有耍任何诡计,在一个短暂的准备后,他的确做到了合法的用任意语言编程。一个18岁的孩子怎么会能掌握所有语言?
上图是一个烧脑语言编译器, 由Daniel B Cristofani(不是我那个同学)使用烧脑语言编写的。
虽然我对他的壮举一直记忆犹新,但是我并不感到惊讶。当学过了一些编程语言后,我才发觉这些语言并没有我期待的那么多样性。再更进一步学习之后,我开始欣赏藏在计算和编程语言原理背后的那个模型,它使得不同语言虽然从同一个简单的原理之根萌发,但却开出了不同的语言之花。这些天我给学生们的标准建议就是“去学“万能”语言”。
鉴于新年临近,很多人会定下新年的计划,比如去学习Go语言或者Rust或者别的语言,那我想给大家一个计划:去学习”万能“语言!希望这篇文章能在你的学习之路上帮到你。
免责声明 :
-
这其实不是要凌驾于500多种编程语言之上成为“万能”语言。 这是为了能够自信的脱离语言本身,而深入的理解通用模型和实现模式。
-
这是一条漫漫长路。这依赖于你现在水平,也许你会在这一年进步飞快,也许会有可能需要10年哦(another ten)。
-
有些概念可能看似目前和你没关系,那么跳过它。
-
根据你的工作和/或目标,这这个学习有可能对你的工作和/或目标起作用,或者不起作用。
为什么这样做呢
如果你视自己为一个用大部分职业生涯时间来读代码写代码的人,你将它归功于自己对于编程语言的普遍熟悉,那么以下几点好处将对你甚为有益:
-
即使你自己不去挑选语言,最后也有有可能使用大量语言。
-
在给定选择的情况下,你能够更高效的选择正确的工具进行工作。
-
随着流行语言的潮起潮落,如果不让自己被语言局限起来,对于工作,公司和项目你会有更广泛的选择空间。
-
很多影响力很大的工程需要对于编译器和语言的深入理解,如通用目标语言的实现以及支持库的生成,领域编程语言,数据库,浏览器,集成开发环境,静态分析工具等等很多。
对我而言,最后一点最为重要。Ras Bodik在伯克利大学教编译器课程的时候一定会向他的学生着重强调这一点 https://youtu.be/MjR7tQTIWbc?t=13m17s
别当一个样本程序员,要去为用户和其他程序员开发有用的工具。就拿纺织和钢铁工具的历史来说:你是想建造机械和工具还是想仅仅操作这些机器?
开始之前:停止称自己为Rails(其他语言)的开发者
这一步很简答,但很重要。也许你对于自己掌握一门语言或者一个技术十分自豪,自认为自己是某个语言的专家,解决了一个又一个难题,称自己为软件工程师并且在任何环境下为维护这个称号而努力着。
Alex Gaynor热爱Python,他担任Python基金会的董事多年并且为Django和PyPy做了大量的贡献,但是这并没有妨碍他使用了多年的ASP语言帮助USDS部门 https://alexgaynor.net/2016/dec/23/looking-for-work/。他称自己为软件工程师,你也应当如此。
第一步: 元化
有一个老笑话,说一个应用物理学家发现他自己在一个弦理论的会议上,他转向一个理论物理学家问道:“你们是怎么做到以11维来思考事情的?”,理论物理学家回答道:“很简单,我们先想象着是N维,然后用11替代N维就行了。”
厉害的程序员也是用同样的技术,你也许把Go语言视为新的并且有挑战性难度的语言,然而厉害的程序员把它看作是带垃圾回收的静态编译类型语言与CSP并发性特性的集合。Switf语言是一个精心设计的新语言,但是厉害的程序员能够快速地掌握,因为它就是一个用LLVM实现的通用目标语言并提供了面向对象特征如协议等。
语言的元层次是大学编译器课程。但是这个字眼有些误导:首先,课程不是严格的学习编译器的机制,它主要目的是让大多数学生深入理解语言。第二,有些人看到动态,编译会觉得矛盾,就会认为编译器课程不会教他们如何使用动态语言,其实这是不对的,这两者有很大的重叠部分,而且大多数编译器课程都有关于虚拟机和字节码的章节,但是这些都没能体现在“编译器”这三个字中。
对于那些没能够有机会上编译器课程的人,有很多好书和在线课程可以参考。我尤其推荐(Alex Aiken’s course )https://lagunita.stanford.edu/courses/Engineering/Compilers/Fall2014/info 之前在Coursera,现在在斯坦福自己的MOOC平台的Alex Aiken课程。另外Berkeley的CS164课程也是很好的选择。但是不幸的是Berkeley现在停止发布新课程的视频了,但是Ras Bodik的2012课程还是可以看的(Ras Bodik’s 2012 session)https://www.youtube.com/watch?v=MjR7tQTIWbc&list=PL3A16CFC42CA6EF4F。
编译器领域规范教材就要数《编译器:原则,技术和工具》(Compilers: Principles, Techniques & Tools)了,俗称“龙书”。像所有规范教材一样,它有狂热的粉丝和深思的批评者,我的总体观点是这是一本最好的书,能够覆盖你事业的多个阶段领域。Myles尤其喜欢《语言实现模式》( Language Implementation Patterns )这本由Terence Parr所著的书,它更加面向那些为开发自己的DSL语言项目而实践的软件工程师,所以你会觉得把它作为第一本书比龙书更平易近人。龙书依旧是编译器相关书籍中,你能选择的最好的书,没有之一。
对于那些身在旧金山并且喜欢亲临课程学习的人,你会对Bradfield的课程感兴趣(languages, compilers and interpreters course.)http://bradfieldcs.com/courses/languages/
第二步: 选择原型语言
如果有一个好的理论基础,那么可以很轻松的学会一门语言,但如果要学500种语言就没有那么轻松了。问题的关键在于要知道并掌握那些跨越各种语言背后的设计思路和通用模式。当选择好了这种原型语言,那么学习一个新语言就很简单了。
Peter Norvig( makes his own suggestion )http://norvig.com/21-days.html 给了他所认为的重要的模式和原型语言:
要学习掌握至少6种编程语言,包括一个强调对象抽象的语言(例如Java或者C++),一个强调功能抽象的语言(像Lisp或者ML或者Haskell),一个支持语法抽象的语言(例如Lisp),一个支持声明特性的语言(像Prolog或者C++的模板),和一个强调并行的语言(像Clojure或者Go)。
这在我看来是一个很好的起点,但你也许会想进一步学习更广泛的内容。
第一,我建议尽可能的早学习C语言。C语言影响很大无处不在使用,不论如何,它能够让你更加轻松地学习其他的语言(特别是C++语言)。有关C语言的基本原理可以参考这里(here,),其他自学的相关建议可以参考这里 https://blog.bradfieldcs.com/the-cost-of-forsaking-c-113986438784#.7ed6k3f2e。
我还建议学习一门汇编语言,MIPS比较简单上手或者比较使用的X86汇编语言。这会让你对于计算机体系结构更加了解而不是语言,但是这些汇编语言为日后你理解那些高级语言特性的实现提供了最底层的解释基础。也许有一天我会建议你学习LLVM这种中间表达语言而不是汇编语言。
Norvig 建议学习一门声明性的语言,确切的说应该是学习一门逻辑编程语言。这可以是像他建议的Prolog语言或者是《 The Reasoned Schemer》一书中miniKanren语言。
在我看来,Norvig建议的并行语言比较好的选择就是CUDA语言。这种并行已经完全超越了你平时所见的4核心CPU,是一个新的GPU结构体系,这种并行编程常用在机器学习中,然而CUDA的学习和其他也强调并发性的语言不同。当然,Go, Clojure或者 Erlang也是不错的选择。
以数组为基础的编程是另一个有用模式. 鉴于这种编程模式大多用在数值计算领域,Norvig有可能忽略了它们。但我认为它们作为与其他语言完全不同的初学原语来说,对于非数值编程者来说也是有用的。APL/J/K/Q等十分费脑的原型语言就是此类语言之典范,不过Matlab/octave会让你更加容易上手。
另外,虽然不算是个语言范式,但是能够熟悉一些非通用目标语言还是值得的,它能够让你在找不到别的解决方式时,用一个非通用目标语言来解决一个问题。Frink是我个人最爱,AWK是另一个典范。
实在是不胜枚举!很多人会坚持说Forth也是一个必要掌握的语言,它能够让你理解基于堆栈的语言。我个人认为动态语言中关于基于堆栈的虚拟机部分已经讲了足够多的堆栈语言内容。也许其他人会告诉我又忘记了其他什么,不过这不是一个准确的语言清单,只希望它能够帮助你入门即可。
第三步:练习
制定一个学习语言的清单很简单,但是掌握这些语言才是最难的部分。如果你足够幸运,你可能在你的工作或者项目中使用了这些中的一部分语言,对于其他人,我认为你们必须努力学习并且有目的的练习。如果不学习,那么进步会很慢;如果不练习,理解不会根深蒂固。
学习一个新语言的好方法就是好好阅读 Hyperpolyglot和earn X in Y Minutes中的内容。这些资料能够介绍语言的关键点并帮助你跨越语法障碍。如果你已经掌握了某类语言家族中的一个,Hyperpolyglot里面的语言要素对比功能能够让你更快的适应新的语言。
另一个有价值的事情就是去探索语言背后的设计思路。这会让你更加清晰的了解每个语言的设计目的,并且给你足够的动力去学习它。例如,如果你对于C++的学习犹豫不断,并且对Bjarne's的设计目的产生疑问,那么你就必须要阅读他《The Design and Evolution of C++》一书。对于大多数语言来说,你都可以查阅到它们被设计之初的动机。
在尝试学习新的语言之前先理解这门语言的设计原理能够让你在探索这门新语言时保持一个清醒而开放的状态。这本书是C++的经典范例,其他语言也有各自的资料。
这之后,你可以或者阅读参考手册或者直接跳到解决具体小问题的实例部分。
对于参考书籍很难给出一个通用的学习建议,但我觉得挑最老的书籍学习吧。大多数规范书籍都是针对有经验但只是学习某个新语言的程序员,这些书会有不少人吐槽,不过老书能够更加有效的把设计语言的重要想法和设计取舍传递给读者,并且大多时候是由语言设计者或者关键实现人员编写的,而新书大多更关注于应用或者某个方法的优化。
掌握一门语言最快速的办法,在我看来,就是找到一个小问题集合,然后一点一点的用这个语言解决它们。Exercism.io 是一个好的资源,它甚至对你打算学习的语言的问题集合有相应的测试程序。有些小问题可以方便的适用到你的工作中,其他问题可以用来对自己水平做自测,总的来说,这些问题集合可以涵盖语言的绝大部分内容,并且助你跨域语法熟悉障碍。
一旦你熟悉了这门语言并且适应了对应的语法,我建议寻找一个大点的项目,准确的说应该是这个语言所面向的问题。如果一个系统语言例如C或者Go,那么你你应该是想写一个命令行工具使用大量的系统调用,如果是C++语言那么可能是一个光线跟踪项目,如果是像Python或者Ruby这样的脚本语言,那么有可能是一个不强调性能的算法问题比如tic-tac-toe AI或者其他的。
坚持寻找
鉴于世界上现有语言的数目,以及老式语言如C语言的不断使用,我们可以轻松地得出一个结论:我们已经发明了足够多的语言来满足自己的需求。这是一个令人吃惊的结论。
在我们迄今为止能够成功指挥的计算设备和我们已知的可计算设备之间存在一个巨大鸿沟。但这不是因为资源的缺乏:在摩尔定律和云资源的便利访问性下,我们有足够多的计算设备可以去做比现在正在做的事情更复杂,影响更巨大的工作,然而问题的关键在于我们和机器的接口,即语言和我们驱动机器的工具。
Gerald Jay Sussman 在他精彩的谈话中提出这个观点:我们其实不知道如何计算( https://www.infoq.com/presentations/We-Really-Dont-Know-How-To-Compute)。他拿当下一门语言举例(一门很老的语言),这样可能说明问题不在于工具,但是不论任何结论,我们现在工具的确没能让我们解决Sussman所提到的那些高效计算问题,
Kanizsa’s 三角幻觉。 Sussman指出,人脑识别出这个幻觉三角形并不难,但是对于我们的计算机来说,目前是无法做到的。
Chris Granger, working on Eve http://www.chris-granger.com/2016/07/21/two-years-of-eve/是众多令人感兴趣的拓荒者之一,他并不是一开始就打算设计一个描述清单上一条一条想法的程序语言,但是这个语言最后发展成为了一个关键的并且紧密耦合的平台组件,语言对于我们利用计算机是十分重要的,这一点一点也不奇怪。
不论Eve能否成为下一个大的编程平台,不论面向想法的语言是否会成为未来主流,至少我们希望所有的计算设备都在我们的控制之下。就像Ras Bodik一样,我鼓励大家成为这种趋势的一个因子,学习不同的语言而不是某个语言,然后利用你深入的理解去做影响更大的事情。
在写这篇文章的同时,2017年1月份Bradfeild上的languages, compilers and interpreters course https://bradfieldcs.com/courses/languages/ 课程已经有1-2节可以学习了,未来打算在4月再开一个单元。
版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。
-END-