到现在,学过的语言真是多不胜举:C、C++、Java、C#、JavaScript、Shell、Perl、Python、Ruby…。这么多的语言,学都学不完。但是这些语言之间的差异其实也不是特别大。个人感觉对程序员最大的变化就是编程思想。

我觉得语言无非就是告诉计算机应该怎样去完成一件事(描述动作),那么只要你理清楚作这件事情的步骤,就很容易将这件事情描述清楚。至于这些步骤应该如何完成,具体语言的实现可能不同,有些语言实现起来简单,有些复杂,但是终究是可以完成的(比如某个步骤需要使用到Map数据结构,但是C语言就没有,所以要自己搞一个)。这就是为什么很多算法书很喜欢用伪代码来描述算法,就是要做到语言无关。

我自己在编程的时候,也是很喜欢先写步骤(通过注释的形式,另一种方式是画流程图,但是那太费劲了),然后将这些步骤用具体语言描述。这样的好处在于先理清楚再动笔,其实就是一个功能不断细化的过程(分治法Divide and Conquer)。

当然,语言的一些特性对我们的功能描述是有影响的,比如编程范式(面向过程,面向对象,etc.),还有就是语言的动态特性(反射,元编程,闭包,etc.)。其他的,语法差异或者语法糖,真的不是很重要,实现的时候查看一些文档很快就写出来了。

昨天还在思考到底一面语言应该提供什么样的特性给程序员比较合理。其实说到底,一门语言只要有逻辑判断和递归(循环),他就是一门完备的语言。但是在这么多的语言竞争的时代,完备是最低要求,完美才是王道(呃,真正的完美是不可能的,趋向于完美)。不过,不同的语言总有它最适用的场景,比如说C,提供了硬件无关的最小功能子集,就连C标准库中,List,Map这样的数据结构也没有。但是基本上很多的应用场景都需要这些数据结构,所以就有了C++的STL,Java的Collections,更高层的语言,像Ruby和Python,List和Map已经成为语言的内建的数据结构(数据类型)了。不过既然是语言自身级别实现,那么性能和可靠性当然是要得到最大保障的。

编程范式也是。很久以前就写过一篇文章,分析了为什么为产生OOP。

但是语言编写的程序毕竟是在操作系统上面跑的,最终还是调用操作系统提供的接口。不管是语言级别实现的接口也好,还是库(用户自定义)级别实现的接口也好,最终都是系统调用。所以想文件操作,进程线程,网络编程,内存管理等,你会发现其实各种语言提供的接口和功能都差不多。所以学好操作系统,对于快速掌握一门语言是很有帮助的。

今天看到Eric S. Raymond的一篇很老的文章《How To Become A Hacker》,其中在介绍学习如何编程的时候讲到:“但是注意,如果你只会一两门语言,你将不会达到黑客所要求的技术水平,甚至也不能达到一个程序员的水平——你需要学会如何以抽象的方式思考编程问题,独立于任何语言。要做一名真正的黑客,你需要学会在几天内通过一些手册,结合你现在所知,迅速掌握一门新语言。这意味着你应该学会几种截然不同的语言。”

“学会如何以抽象的方式思考编程问题,独立于任何语言”,这句话到处了编程的精髓。举个例子来说,如果要完成这么一项工作:分析某个网页有没有包含某个关键词。

要完成这么一件事情,步骤如下:

  1. 将该网页抓取下来(下载到本地):download(url, dest);
  2. 分析给定文件是不是包含某个关键词:match(source, pattern); 至于这些步骤应该怎么实现,各种语言有不同的实现方式。但是,只要事情足够细小独立,我们就很容易完成。这就是分而治之的好处。

分治法的一个具体例子就是模块化和OOP。能够识别出完成一件事情需要具备哪些功能,将这些子功能封装到类中就是所谓的OOP。这种思想其实很常见,比如我们编译器本身,编译过程一般分为如下几个部分:文本分析、语法分析、代码生成和优化。对于解释型语言,如Java,还有一个加载和执行过程。再举个面向应用的例子,一个网站,一般会分为几个模块:首页,文章,图片,用户管理,等等。数据库的表其实也是一种功能划分。