当前位置: 首页 > 科技观察

王音:如何掌握所有的编程语言

时间:2023-03-20 15:22:16 科技观察

是的,我这里要说的不是如何掌握一门编程语言,而是所有的……很多编程初学者还在写信给我问征求意见,问我该怎么办?学什么编程语言,怎么学。由于我知道如何掌握“所有”编程语言,我总觉得“一种”学习什么语言的问题比较低级,所以我一直没有时间回复他们:P但是渐渐地,我发现不只是白人有这个问题,就连很多美国大公司的高级工程师也没有搞清楚。今天我有动力,想回答这个搁置已久的“首要问题”。类似的话题好像以前写过,现在想再写一遍。因为和很多人交流过后,我对自己脑子里的想法有了更准确的表达(不是翻译成文字)。如果您有以下困惑,那么这篇文章或许可以帮到您:您是编程初学者,不知道该选择哪种编程语言来入门。你是资深程序员或团队负责人,对新兴的语言感到迷茫,不知道该“投资”哪门语言。你的团队正在为使用哪种编程语言争论不休,各种宗教斗争纷至沓来。你追逐潮流,采用了一些时髦的语言,结果两个月后却发现自己身陷泥潭,苦不堪言……虽然我已经不再问这些世事,但不可否认的现实是,编程语言还是很重要的话题,这种情况短时间内不会改变。程序员职位往往需要熟悉某些语言,甚至一些奇葩公司要求你“深入理解OOP或FP设计模式”。对于工作的程序员来说,编程语言仍然是一个令人不悦的宗教话题。它是如此的虔诚以至于当我批评和嘲笑某些语言(比如Go语言)时,有些人会本能地认为我是另一种语言(比如Java)的粉丝。显然我不能成为任何语言的粉丝,我什至不是Yin的粉丝;)对于我以前从未见过的任何语言,我只是拿起它并使用它而无需经过学习过程。看完这篇文章,也许你会明白为什么我能做到这样的效果。了解其中的内容,每个程序员都应该能够做到这一点。好吧,我希望。注意语言特征,而不是语言。许多人关心他们或其他人是否“知道”某种语言。这些问题对我来说都不存在。虽然我写过文章批评很多语言缺陷,但在实际工作中我很少与人争论。如果有人在我旁边争论,我什至会戴上耳机,懒得听他们说什么;)为什么?我发现这归结为我重视“语言特性”而不是“语言”作为一个整体“。我可以用任何语言编写像样的代码,即使是最糟糕的语言。任何“语言”都是组合各种“语言特征”。打个比方,编程语言就像一台电脑。它的品牌可能叫“Lenovo”,或者“IBM”,或者“Dell”,或者“Apple”。那么,你能说Apple一定比IBM好吗?不可以。你要看装的是什么型号的处理器,几核,主频多少,一级缓存多少,二级缓存多少……,内存多少,硬盘在那里,显示器的分辨率是多少,显卡是什么GPU,网卡速度等等各种“配置”。有时你还得看看各个组件之间的兼容性。这些配置对应到编程语言,也就是所谓的“语言特性”。语言特性的一些例子:变量定义算术运算erationfor循环语句,while循环语句函数定义,函数调用递归静态类型系统类型推导lambda函数面向对象垃圾回收指针算术goto语句这些语言特性,就像你在选择一台电脑的时候,看看里面有什么配置它。在选择电脑时,没有人会说戴尔一定是最好的。他们只会说这个型号配备了英特尔的i7处理器,比i5好,DDR3内存比DDR2快那么多。SSD比磁盘快得多,ATI显卡很垃圾……等等。编程语言也是如此。对于初学者来说,无需纠结先学哪门语言,再学哪门语言。曾经有人给我写信问这样的问题,我苦苦思索了几个星期,但我什至没有开始学习一门语言。有了这段纠结的时间,其实他已经可以掌握所有他纠结过的语言了。初学者往往不明白,每一种语言都必须有一套“共同”的特性。比如变量、函数、整数和浮点运算等等。这些在每一种通用编程语言中都是必需的,一个也不能少。只要通过“某种语言”学习了这些特性,掌握了这些特性的基本概念,就可以随时将这些知识应用到任何其他语言中。你投入其中的时间几乎不会被浪费。因此,初学者纠结于“先学哪门语言”。这样的时间不值得。最好只选择一种语言并投入其中。如果你不能使用一种语言的基本特性写出好的代码,它不会帮助你切换到另一种语言。你会写出同样糟糕的代码。我经常看到一些人,他们的Java代码相当乱糟糟,但他们骂Java不好,并且雄心勃勃地想转用Go语言。这些人不明白能写出好的代码的是人,而不是语言。如果你脑子里没有一个清晰简单的心智模型,用任何语言表达出来都会一塌糊涂。如果你写的Java代码很糟糕,你写的Go代码也可能同样糟糕,甚至更糟。很多初学者不明白,如果一个好的程序员开始使用一门新的编程语言,他往往不是去读这门语言的大部头手册或书籍,而是先有一个问题要解决。手头有问题,他可以在两分钟内浏览该语言的手册,看看该语言长什么样。然后,他直接拿起一段示例代码开始修改,试图将代码改成自己要解决的问题。在这短暂的过程中,他很快掌握了这门语言,并用它来表达自己的想法。在这个过程中,随着需求的出现,他可能会提出这样的问题:这种语言的“变量定义”的语法是什么,我需要“声明类型”,还是可以使用“类型推导”?它的“类型”语法是什么?它支持“泛型”吗?如何表达泛型的“变异性”?这门语言的“函数”的语法是什么,“函数调用”的语法是什么,可以使用“默认参数”吗?...注意到了吗?上面每个引号中的内容都是一个语言特性(或概念)。这些概念可以存在于任何一种语言中,尽管语法可能不同,但它们的本质是相同的。例如,有的语言把参数类型写在变量前面,有的写在后面,有的用冒号隔开,有的则不用。这些实际问题是写了实际的代码,解决了手头的问题之后自然而然地带出来的,而不是一开始就仔细阅读语言手册。因为掌握了语言特性的人都知道,自己需要的特性在任何一种语言中都必须有相应的表达。如果没有直接的表达方式,那么一定有某种“旁路”。如果有一种直接的表达方式,那只是语法略有不同。于是,他带着问题去寻找特性,就像查字典一样,而不是沉浸在一大堆手册中,在昏昏欲睡一个月后开始写代码。掌握了通用的语言特征后,就只剩下某些语言“特有”的特征了。学过语言的人都知道,设计新的、好的、无害的特性是非常困难的。所以一般来说,一门好的语言终究不会有那么一两个独特的新特性。如果一种语言声称拥有超过5个新特性,那么你就要小心了,因为它们带来的总和可能不是优势,而是灾难!同理,最好的语言研究者往往不是某种语言的设计者,而是某种关键语言特性的设计者(或支持者)。比如著名的计算机科学家Dijkstra就是“递归”的坚定支持者。现在所有的语言都有递归,但是你可能不知道早期的编程语言是不支持递归的。直到Dijkstra强烈敦促Algol60委员会增加对递归的支持,这种情况才有所改变。TonyHoare也是一名语言功能设计师。他设计了几个重要的语言特性,但没有设计任何语言。另外,别忘了还有一位叫王寅的语言专家,他是union类型的早期支持者和实现者,也是checkedexception特性的支持者。他在他的博客文章中指出了检查异常和联合类型之间的区别。关系:P很多人盲目崇拜语言设计者。只要他们听说有人设计(或“发明”)了一种语言,他们就会感到兴奋和钦佩。他们不明白,其实所有的编程语言都和戴尔、联想一样,只是“组装机”而已。语言特性的设计者是Intel、AMD、ARM、Qualcomm等核心技术的创造者。合理的入门语言所以初学者要想事半功倍,就应该从没有明显严重问题的“合理”语言入手,掌握最关键的语言特性,然后将这些概念应用到其他语言中。什么是合理的入门级语言?个人觉得这几门语言可以入门:SchemeCJavaPythonJavaScript那么相比之下,哪些语言我不推荐入门呢?ShellPowerShellAWKPerlPHPBasicGo一般来说,不应该使用所谓的“脚本语言”作为入门语言,尤其是那些早期Unix系统衍生出来的脚本语言工具。虽然PowerShell比UnixShell更高级,但它仍然没有摆脱脚本语言的根本问题——他们的设计者不知道他们在做什么:P抓不住关键。脚本语言往往会在语法中加入一些系统工具(比如正则表达式、Web概念),导致初学者在上面浪费太多时间,却不理解编程最关键的概念:变量、函数、递归、types……不推荐Go语言的原因也是类似的。虽然Go语言不是脚本语言,但它的设计者显然不明白他们在做什么。所以用Go语言学习编程,不能把注意力放在最关键最完美的语言特性上。掌握关键语言特性,忽略次要特性。为了达到我前面提到的精通效果,初学者应该把注意力集中在语言中最关键的特性上,而不是被次要的特性分散注意力。举个夸张的例子。我发现很多编程培训课程和野鸡大学的编程入门课程经常教学生如何使用printf打印“HelloWorld!”,然后让他们记住printf的各种“格式字符”的意思,并要求他们实现各种复杂格式的Printouts,甚至需要打印成文本文件,然后再读出来……但殊不知,这种输出输入操作根本不是语言的一部分,对于掌握核心概念来说是次要的的编程。有的人的Java课程已经上了好几个星期了,还在布置各种printf作业。学生写了几百行printf,却不懂什么是变量和函数,甚至不知道如何使用算术语句和循环语句!这就是为什么许多初学者觉得编程困难的原因。连%d、%f都不知道,连%.2f的意思都记不住,还怎么学编程!然而,这些野鸡大学的“教授”头衔太洗脑了,以至于他们教过的学生(比如我女朋友)都来找我请教,骂我教的都是些没用的东西,连printf作业都做不完学完后:P不要给我讲for循环,函数之类的。。。能不能等几个月,让我背熟printf的用法?又学那些?所以你发现,一旦被坏老师教过,程序员基本上就完蛋了。即使遇到良师,也很难改正。当然这是一个夸张的例子,因为printf根本不是一个真正的语言特性,但它是一个例子,从同样的角度说明了语言特性不那么肤浅的问题。以下是一些次要语言特性的例子:对于C语言中的语句块,如果其中只有一条语句,则不需要使用大括号。如果Go语言的函数参数类型相同,可以组合起来写在一起,比如funcfoo(sstring,x,y,zint,cbool){...}Perl使用正则表达式作为语言的特殊语法JavaScript语句有时句末的分号可以省略Haskell、ML等Currying语言自己实现语言特性在基本学习了各种语言特性并能够使用它们编写代码之后,下一步是实施它们。只有实现了各种语言特性,你才能完全拥有它们,成为它们的主人。否则你只是它们的用户,你将被语言设计者牵着鼻子走。有位高手说得好,要完全理解一门语言,最好的办法就是自己去实现,也就是写一个解释器来实现它的语义。但是我觉得这句话应该稍微修改一下:完全理解一个“语言特性”的最好方法就是自己去实现它。请注意,我在这里将“语言”更改为“语言功能”。你不需要实现整个语言来做到这一点,因为我们最终会使用语言特性。只要你自己实现了一个语言特性,你就能理解这个特性在任何语言中是如何实现和使用的。比如在学习SICP的时候,大家都会用Scheme来实现一个面向对象的系统。用Scheme实现的面向对象系统与Java、C++、Python等语言的语法相去甚远,但它可以帮助你理解任何这些OOP语言中“面向对象”的概念,甚至可以帮助您了解各种面向对象实现的差异。这种效果是直接学习OOP语言无法获得的,因为在学习Java、C++、Python等语言时,你只是一个使用者,而使用Scheme自己实现OO系统后,你就变成了一个创造者。类似的特性包括类型推断、类型检查、惰性求值等。几乎所有的语言特性我都实现了,所以任何一种语言在我面前都是一个可以随意拆装的玩具,不再是凌驾于我之上的神圣之物。总结写了这么多,把重要的词重复三遍:语言特征,语言特征,语言特征,语言特征!无论你是初学者还是高级程序员,都应该关注语言特性,而不是纠结于整个“语言品牌”。只有这样,我们才能做到精通,拿起任何一门语言几乎立即使用,写出高质量的代码。