如何掌握编程语言

学习编程语言的人往往会有以下几种心理,这样会觉得自己有学不完的东西,或者会走错路。我来简单分析一下这些心理,希望能消除一些疑惑。

1.追求“新语言”。

基础哲学告诉我们,新事物不一定是“新事物”,但可能是历史的倒退。事实证明,很多新的语言还不如已经存在很久的语言。正视这个事实,有多少现代语言的“新概念”在一些最古老的语言中并不存在?编程语言就像商品,家家户户其实都在做广告。大多数设计,包括一些最“难”和最“理论化”的语言中的概念,可能是肤浅和短暂的。如果你看不透这些东西的设计,你就会被他们忽悠。过分的热情和过分的宣传往往意味着肤浅。很多语言设计人员并没有真正理解编程语言设计的原理,所以在设计中经常出错。但为了推广自己的语言和体系,他们必须说大话,进行宗教宣传。

2.“存在即合理”。

我记得一个著名的人说过,“不需要一种不能带来新的思维方式的语言。”他说的很对。世界上这么多种语言,哪些带来了新的思维方式?其实很少。大多数语言给世界带来的只有混乱。可能有人会反驳,“你怎么能说语言A不需要存在呢?我想用的库L没有其他语言支持,只能用a .”但是注意他说的是存在的“必要性”。

如果把存在的“事实”当成存在的“必然”,那就不合逻辑了。这就好比如果二战我们没有打败希特勒,现在我们都是他的奴隶,然后你说:“希特勒应该存在,因为他支持我们。”显然这个逻辑有问题,因为如果历史走另一条路(也就是希特勒不存在),我们就会过上自由幸福的生活,所以希特勒不应该存在。比较一个事物存在与不存在的两种可能后果,然后做出判断,才是正确的逻辑。按照这样的推理,如果设计糟糕的A语言不存在,那么设计更好的B语言很可能会得到更多的支持,从而实现甚至超越L库的功能。

3.追求“新功能”。

编程语言的设计者总是喜欢“发明”新术语,大肆宣传。普通程序员往往看不出,这些“新概念”其实大多有着深刻时尚的外表,却没有实质性的内涵。通常,刚学完一种语言A,另一种语言B就来了,说它有一个叫XYZ的新特性。于是你又开始学B,以此类推。在业内人士看来,这些所谓的“新功能”大多是新瓶装旧酒。许多人在写论文时喜欢这个标题:XYZ:一种新颖的方法...这引起了概念的爆炸,但没有实质性的进展。可以说这是计算机科学最致命的短板。

4.追求“小动作”。

很多编程书都喜欢炫耀一些小技巧,让程序看起来很“短”。比如他们会告诉你“(i++)-(++i)”应该得到什么结果;或者考察运算符的优先级,说可以减少括号;或者告诉你“如果后面只有一行代码,可以不用花括号”,等等。不知道这些提示其实大多是编程语言设计的失败或者历史遗留问题。他们带来的不是清晰的思维,而是逻辑混乱和认知负担。比如C语言的++运算符出现,是因为C语言设计者使用的计算机内存少得可怜,“i++”明显比“i=i+1”少了两个字符,所以他们认为这样可以节省一些空间。现在我们已经不缺那段记忆了,但是++运算符带来的困惑和迷茫却流传了下来。现在一些最新的语言也喜欢玩这种语法把戏。如果你追求这些技巧,往往会错过本质。

5.对于“专门领域”。

很多语言都没有什么新意。为了占据一方土地,他们自称适合某个特定的任务,比如文本处理、数据库查询、Web编程、游戏设计、并行计算,或者其他专门的领域。但是我们真的需要不同的语言来做这些事情吗?其实这些事情大部分都可以用相同的通用语言解决,或者在现有语言的基础上稍作改动。然而,由于各种政治和商业原因,不同的语言被设计来占领市场。就学习而言,他们其实是不相干的,他们带来的“多语言合作”问题几乎掩盖了他们的好处。事实上,你可以从一些设计良好的通用语言中学习到所有这些“专用语言”的精髓。后面我会推荐一两个这样的语言。

我必须指出,以上心理不仅对自己有害,对整个行业也是有害的。当被这些观念教导的人进入公司后,他们会开始把这些曾经害怕的东西变成教条,用来筛选新人,这样就会形成恶性循环。

如何掌握所有编程语言

老实说,我对几乎所有风格的编程语言都有专家意见。它们在我心目中是如此简单,以至于我不再是任何语言的“支持者”,包括函数式语言。但我花了太多时间探索这条路,我希望能提炼出一些能帮助人们在短时间内达成这种共识的“小窍门”。具体细节足够写一本书了,我现在在这里只提出一些初步的建议。

1.注重“本质”和“原则”。

就像所有的科学一样,编程语言的本质原理只有几个,但可以用来构造很多复杂的概念。但是,人们往往忽略了简单原理的重要性,匆匆看完就追求最新的复杂概念。他们没有注意到,大多数最新的概念其实可以和最简单的结合起来。然而,对基本概念的一知半解导致他们看不到那些复杂概念的本质。例如,其中一个概念是递归。国内很多学生只理解像汉诺塔这样的程序中的递归,但对递归的效率也有很大的误解,认为递归不如循环高效。其实递归比循环表达式强多了,效率也差不多。有些程序,比如解释器,没有递归很难完成。

2.实现一种编程语言。

学习使用工具的最好方法是制作工具,所以学习编程语言的最好方法是实现编程语言。这个不需要完整的编译器,只需要写一些简单的解释器就可以实现最基本的功能。之后,你会发现你大概知道如何实现语言的所有新特性,而不仅仅是在用户层面。实现编程语言的最快方法是使用类似Scheme的语言,在这种语言中,代码可以作为数据使用。它允许你快速编写一个新的语言解释器。我的GitHub里有一些我写的解释器的例子(比如这个短代码实现了Haskell的懒惰语义)。有兴趣可以参考一下。

几种常见的语言风格

我简单说一下几种常见的语言风格及其问题。注意,这里的分类不是严格意义上的,有些可能在概念上重叠。

1.面向对象语言

事实表明,“面向对象”的整个概念基本上是错误的。它的流行是因为最初的“软件危机”(天知道这个危机是否真的存在)。设计的初衷是将“接口”和“实现”分开,这样下层实现的变化就不会影响上层的功能。然而,大多数面向对象语言的设计遵循了一个根本错误的原则:“一切都是对象。”以至于所有函数都必须放在所谓的“对象”中,不能直接作为参数或变量传递。这常常导致需要使用繁琐的设计模式来实现即使对于C语言来说也很简单的东西。事实上,“接口”和“实现”的分离并不需要将所有的功能放入对象中。其他概念,如继承和重载,实际上带来的问题比它们解决的问题更多。

“面向对象方法”的过度使用已经开始对整个行业造成负面影响。很多公司的程序员都喜欢机械地复制一些不必要的设计模式,但实际上他们并没有做什么好事,只是让程序变得冗长难懂而已。我不得不指出,《设计模式》这本书是这一大部分复杂性的罪魁祸首。可惜如此肤浅、没有内容、偷换概念的书,居然被很多人捧为经典。

那么具有高阶函数的面向对象语言呢,比如Python,JavaScript,Ruby,Scala?当然,有了高阶函数,你可以直接表示很多东西,而不需要使用设计模式。但是由于设计模式的流毒思想,一些程序员居然在这些不需要设计模式的语言中使用复杂的设计模式,让人哭笑不得。所以在学习的时候,最好不要使用这些语言,避免不必要的干扰。必要的时候再回来用,可以取其精华,去其糟粕。