论各种编程语言

本人使用过的编程语言,细数包括5个大类,总计超过28种(参见我的技能栈),有些代码写得多,有些写得少,写得最多的是:Java、JS、HTML、CSS、……,其次是C++、C、PHP、C#、Lua、SQL、……,写得少的是Python、VB、Jade、MatLab、……。


下面说一点心得体会(原创、纯手打),供学习、比较、参考。


    首先,看基础语法。好的基础语法,应该是简单直接的、易于记忆和书写的。“功能强”跟“简单易用”,是两个矛盾面——在基础语法中,加入常用增强功能,确实能方便编程,但是也让基础语法变得臃肿。比如说,for循环,有些语言甚至有5种以上的写法,虽然每种写法特点不一样,但是这增加了语言学习和代码维护的负担。如果,它把for循环精简到只有两种,比如 for(i=0;i<n;i++)和 while(flag) 这种通用的结构,那么所有程序中,只有这一种写法,维护起来要容易些。但是,也有一个弊端,比如一些特殊数据结构的循环,集合、链表、Map等,用通用结构去写就会麻烦很多。

    怎么看这个问题?增强语法,绝大多数时候,都可以用封装库函数(工具函数)去解决。我认为,增强程序功能,首先应该从增加库函数入手,而不是增强语法。虽然增强语法比提供库函数,更易用,但要考虑这样做是不是也降低了程序可维护性(因为库函数代码,是标准语法,是大家都看得懂的,而增强语法,如果不学习一下,是不知道怎么回事的)。

    什么时候该增强语法呢?我认为是简单直接的、且常用的、且比提供库函数要好得多的地方,这三个条件缺一不可,反例如下:

  1. 如果这个功能不常用,那么提供一个库函数,哪怕使用麻烦一点又有多大影响呢?何必要去设计一个新语法,用于一个不常用的功能上呢?

  2. 如果这个功能调用库函数的方式也很容易、很方便的做到,为什么要用新语法去做?

  3. 如果为了某一套功能设计出一个非常复杂的语法,那么和直接用库函数相比又有什么本质区别呢,语法的目的就是简化使用,复杂的语法本身也难于使用、难于升级维护,不如不要。

    举个例子,Java 8新语法出来这么久了,其实也没增加多少内容,我尝试用过很多次,但我就是记不住,最终不想用了。为什么?因为Java7、Java6的语法已经很强大了,Java8的新语法只是锦上添花、“语法糖”而已。

    比如 lambda 表达式,形如:

// 标准语法
list.sort(new Comparator<Integer>() {
    public int compare(Integer o1, Integer o2) {
      if(o1>o2)
        return -1;
      else if(o1<o2)
        return 1;
      else
        return 0;
    }
} );
// 增强lambda语法
list.sort( ( o1, o2 ) -> {
    if(o1>o2)
        return -1;
      else if(o1<o2)
        return 1;
      else
        return 0;
} );

    lambda语法这样写省略了3行代码,要简洁一些。但是它让Java代码多了一层不透明的语法包装,而且lambda使用有些限制,没有上面标准语法的功能强大。我认为,一个语法,你不能做到通用、统一,这个语法是很失败的——lambda不能完全取代传统的写法,那么代码中就会存在两种不同的写法,看起来就很恼火。

    正例,Java 7的一些改进则挺好,典型的如泛型的自动推导,这是个很实用的小改进,因为编译器是可以帮我们自动推导出泛型类型的,这个改进让我们节省了以前那种“冗余”的写法,即便是删掉定义的泛型部分,语义也没有任何影响,如下所示:

// 改进前
List<String> list = new ArrayList<String>();
// 改进后
List<String> list = new ArrayList<>();

    一门好的语言,语法应该规则严谨,没有歧义,更没什么黑魔法、变异用法。任何人写出的代码都基本一致,这使得这门语言简单易学、易于阅读和维护。放弃部分“灵活”和“自由”,换来更好的维护性,我觉得是值得的。

    我再谈谈一个极端,比如说HTML,这门语言很简单,大家都很容易学习。简单到什么程度呢?它连for循环都没有。就是这么简单的一门语言,用了这么多年,到HTML5才有一个不算大的升级,而且HTML5的写法并没有给之前版本带来多少改变。即便是20年前的代码,现在依然很容易维护。这是相当成功的。

    为什么HTML不官方支持模板渲染、for循环,就像JSP、ASP那样。其实理论上HTML也可以做的,但它为什么不做。历史不再去深究,但事实证明,有些事情语言本身不去做,也有方法去实现。语言本身做好最基础的事情就行了。

    再说说,一个特殊的例子:Shell脚本。Shell的基础语法并不复杂,只是它的语法有些古老,不够简洁易用(比如写个if后面都还得更一个fi,获取数组长度要用${#arr[@]},当然如果你用熟练之后,其实也是OK的),其功能也不是很完善(比如对数据结构的支持,Map,Set等)。它最主要的作用是编排各种Unix/Linux指令。这种语言,其关键已经不在于语言本身的基础语法了。因为linux命令可以扩展它。但是由于它没有包管理之类的东西,它的运行,必须得依赖于操作系统上各种指令。而且也没有断点调试功能,只能用传统的打印变量的方式跟踪执行。我认为,它的灵活和简陋,会导致它的代码很难维护和大工程化使用,通常一个100行的shell脚本已经相当难维护了——灵活而简陋,意味着,很多功能,比如字符串的常见操作,没有官方的标准API去调用,只能借助各种扩展(比如awk、sed、cut、grep等等),可以这么说,一个稍微复杂点的功能,用Shell实现,10个人写出来的代码,很可能有10个不同版本。而且Shell编程中,确实有很多黑魔法、变异用法,比如“shopt extglob”、"set -eo pipefail"、“xargs -r”( --no-run-if-empty,this option is a GNU extension),不同的操作系统上,不同的shell版本(bash、ksh、tcsh、ash、dash等)也会有一定区别。

    最后再说一说,面向过程和面向对象的编程语言。我觉得,面向过程的编程语言,适合中小型、微型项目,而面向对象的编程语言,更适合中大型以上规模的项目。我们知道,面向对象的语言有三大特性:封装、继承、多态。这三大特点,可以说是工程化的设计思想体现,对代码进行分类和复用的方法,它是从大工程项目中总结提炼出来的套路。在很小的项目中,也可以按这个思路来,但是会显得有些繁琐和不够直接。如果是做一个大项目,开发的人很多,代码量也很大,强烈建议用面向对象的编程语言,甚至像JavaScript(ES 5)这样大规模被使用的语言,其实都不太适合大项目,真的很难维护!所以才有了ES 6、CoffeeScript、TypeScript等改进版本。

    

    总结:说了这么多,心里应该有数了。我再补充几点:

1、学一门编程语言,首先只学“基础语法中最基础的那些”,先不要碰那些扩展的语法,和高级语法。

2、遵循大多数人遵守的编程语法和习惯,不要利用语法技巧,写出让人不太好理解的代码。

3、代码量、执行效率、可维护性三者的选择上,首选可维护性。(下面特别说明!)

    不要为了节省代码,而降低可维护性,也不要为了提高一点点运行效率而降低代码的可维护性(很多人在这一点上会犯错——包括我,在我刚开始工作的那些年,受到《Effective C++》《Effective Java》等的影响,追求代码的极致运行性能,甚至至今,我写if-return时都要节省一个else,用Link List更好的时候我就不会用Array List,还有,我经常用一些奇葩的写法去改造传统的代码,以提升程序的性能。但是后来我经常看别人的代码,经常改别人的代码——各种语言的代码,我站在程序员和管理者的双重角度看,代码的可维护性优先级应该要高于执行效率,这个问题我也同一些资深架构师讨论过,现在的CPU执行速度非常快了,为了节省程序理论上的一两个指令执行时间,而采用非传统的写法,是弊大于利的,甚至大部分时候,O(n)和O(n^2)复杂度,执行时间都没有本质差别,木桶效应告诉我们,要提高效率应该从最慢的地方入手,比如网络、IO,而不要去纠结O(n)和O(n+k)、O(n^2)的差别)。


© 2009-2020 Zollty.com 版权所有。渝ICP备20008982号