某天看到一篇博文,《一百年后,人类怎样编程? 》, 只是这个题目,就勾起心中无限感慨。文章没细看,内容大致是分析各种语言,以及其中各种语言现象,今后的发展趋势。我对于语言的进步一直不感冒,对5年前 就有很多人推崇的 Ruby,至今也懒得抬眼皮看看,8年前被迫用过几天 Perl,我就断定这是最糟糕的编程语言之一,因为它标榜自由,却又没法真正自由。时至今日,我仍然只用 C++,C#,Java 这三种语言,如果 SQL 也算的话就是四种。对于达到一定程度的程序员而言,语言已经不重要了,不管做什么功能或者什么平台,只要不是初次上手,都应该有50%以上的代码可以自动 生成出来,另外利用开源代码和商业化构件完成30%以上的工作,真正需要自己手工编写的部分绝对不应超过 20%。不论是自动生成的代码,还是开源代码或构件,最大程度的可理解性和通用性是首要追求的目标,因此最通用的,和使用人数最多的语言才是最好的语言。 语言的进步对于提高编程效率确有一定帮助,我自己也深有体会,六年前我做 C# 项目的时候不得不自己写了对 IList 进行查询的功能,两年之后,LINQ 成了语言自带的标准功能,后来的程序员显然可以节省开发这个功能的时间。但是,语言带来的效率提升,远远不如思考方式变化引起的编程效率飞跃来得大。
从第一天编程开始,我就不喜欢这个工作,看到同事飞快地打键盘,屏幕不停地吐出一行行程序,觉得这件事实在傻透了,她编的是 FOXPRO,又是一种我很看不起的语言。她编的功能,无非就是横竖画上几根表格线,然后把一些数字和文字填到正确的格子里去,这就是公司里的编程高手所 做的事情。我曾经惊讶于这么傻的事情竟然真的需要人来做,可是如果不用人做,又能怎样呢?那时幸亏我利用一点小聪明,在还没有开始从事这种傻工作的时候, 就改去研究解密算法了,后来又混上了设计师,小经理,总算没有傻掉,那时心里不免暗自得意和庆幸。
2000年,有幸目睹了一位当时国内最牛程序员的一次编程作业,从此彻底颠覆了我的想法。先说说牛人的业绩,一个工作日,基本没加班,完成一个 复杂 C/S 软件的服务器端,用统计小工具数数代码,三万多行。这个软件经过简单的测试,第二天就上线实际运行了,每天数千人访问,没出过大问题。再说开发过程,开发 环境是 VS6.0,牛人很少动鼠标,大概嫌耽误时间,各种快捷键运用,让人眼花缭乱,程序基本上不是写出来的,而是粘贴过来,重新排列组合一番,再敲上几个语句 补充修正一下,就算大功告成。搞定一个程序块的时间,基本上跟一般人写一条语句的时间差不多。整个工作过程中,看不出明显用于思考的时间,只要不离开座 位,键盘的声音就一直连续不停。我想牛人之所以牛,关键就在这里,像运用语句一样运用语句块,程序不是写出来的,而是装配起来的,就产生了如同手工组装劳 斯莱斯与模块化装配丰田之间的巨大生产率差异。我那时和牛人不在同一层办公,平时很少机会接触,又一次在楼下食堂吃饭正好坐邻桌,听到牛人讲起一件往事, 牛人多年来,不论在哪里工作,都要带一块自己的硬盘,里面有几 GB 以往做的程序--他的 code base ,有一次这个硬盘突然卡壳了,牛人就跟老婆说,咱们准备回老家改行干别的吧,结果没过太久,那个硬盘自己又恢复了,所以牛人终于没有回老家去。
可见,如果没有 code base ,牛人立刻就不牛了。后来我又见过不少优秀程序员,使用自己的 code base 装配出一个个巨大复杂的程序,这种做法局限性也很明显,自己的 code base 终究有限,总有不够用的时候。既然如此,利用别人的 code base 不就解决问题了吗?理论上是这样,但现实中却完全不是这么回事,我很少见到大量利用别人 code base 的编程高手,倒不是这些高手清高,而是他们常常觉得与其看懂人家的程序,还不如自己写来得快,节省一点打字的时间,就要为了适应别人的思路花更多时间思 考,得不偿失。可以说,到了这个程度,code base 的大小基本上决定了水平的高低,顶级的牛人都有上百万乃至数百万行规模的 code base ,俺到今天才攒了50万左右,离牛人们还差得很远。按照这个道理,只要时间足够长,总会有一些牛人可以积攒一个足够大的 code base ,穷尽当代人类能够想象到的所有程序,这个时候就没有编写,只有装配了。如果软件由编写变成装配,那么接下来一个自然的发展就是装配也要自动化,2000 年的时候,代码自动生成工具还不发达,到2005年,基于模板的代码生成工具已经遍地开花了。然而这一切似乎只是历史的重复,模板语言似乎变成了又一种高 级语言,仍然需要人工编写,导致牛人们的 code base 当中又多了一些这种模板而已。而且也总有一些例外情况,用模板做起来复杂无比,还不如干脆留着手工完成。
计算机是否可以自己组装程序呢?现在看来,似乎已经很接近了,至少从 UML 生成代码框架已经很成熟了,而框架里面需要填入的东西,正是 code base 的内容。现在缺少的,只是找到正确的代码块,作一些必要的修正,填入框架中正确的地方的问题。如果 code base 中所有的代码块都有正确的形式化描述,代码框架的每一处地方也都有这样的形式化描述,把二者做一个匹配不就完成了装配工作了吗?至于需要必要的修改的地 方,通常用编译器检查就能找到。如果真是这样,那么这件事早就成功了,像 IBM 这样的公司,一直就想做成这件事,而且他们并不乏完成这些工作所需的任何资源。
然而,时至今日,软件开发不管怎么自动化,总是有一些例外,需要程序员去手工处理。这些例外情况,通常无关乎高精尖,而是些很普通的问题。在八 年以前,我还没有接触知识表示和人工智能的时候,这个问题一直在脑中挥之不去。2003年,偶然接触到 cyc 项目,这又一次彻底颠覆了我的想法,因为这个 cyc 刚好能作一些看起来很简单,却又非要人工才能处理的事情,而且这些事情并不像看上去那么简单,一个简单的推理常常要调用成千上万条断言。当然 cyc 并不是一个真正的常识处理系统,它固然是十余年积累的成果,也有很多闪光的思想,但是局限性也很明显。不管怎样,它为我开启了一个全新的视野。人工智能是 个很大的领域,其中有很多天才的创见,要理解它的全部内涵,是件艰巨漫长的工作。然而,有一件事情从开始的时候就能得出结论,那就是,如果计算机真的具有 了与人类相当的智能,那么必然就不再需要人来为它编程序,那个时候,就是程序员这个职业寿终正寝的时候,当然,整个软件产业也将不复存在。所以,程序员以 及软件产业的生存,其实就寄托于那些为数越来越少的,必须人来处理的“例外”情况。
我们现在就来关注这些例外情况,因为它们是如此重要,将会决定各位以及产业的命运。
软件是什么呢?计算机发展的早期历史上是没有软件的概念的,那时候只有程序,每个用户就是他自己的程序员,编写程序满足他自己的需求。这个时候 的程序员,不需要需求调研,不需要划分工作阶段,总之一句话,想怎么干就怎么干,他们也不会考虑复用,因为程序只是他们个人想法的表达,没有想法的时候想 也没用,一旦有了想法,两下就写出来了,即使需要借鉴以前的想法,从脑子里调出来比从故纸堆翻出来也快捷得多。也许软件与程序的不同就在于此,软件是做给 别人用的,程序是写给自己用的。软件是伴随着不会编程的“业余”用户的产生而出现的。开发软件与写程序第一个不同的地方就是要做需求调研,不管做多简单的 软件,都要调研。有的时候,程序员看似没有做,其实是他和用户已经很熟悉,用户的需求早已经都记在脑子里了。用户有需求就表明用户有一些需要计算的问题, 这些问题可以由计算机做,当然也可以由人来做,事实上 computer 最早指的是拿着纸笔或者计算尺工作的计算员们。如果由人来完成计算,用户通常需要告诉计算员计算的公式和流程,然后提供初始数据,如果这位计算员经验丰富 的话,有时候不必如此罗嗦,只需要告诉他算什么题目就可以了,计算员自己知道公式和流程,或者即使当时不知道,也可以自己找资料学习。使用计算机就享受不 到如此的便利了,计算机不会自己学习查资料,即使硬盘里存有以往的计算程序,它也不会自己去使用,一定要人手工调出来运行。人与计算机的根本差别不在于处 理信息的能力,而在于处理信息的主观能动性。
自从引入了客户,引入了需求,软件开发开始变得复杂了,最早的客户还比较好应付,他们都是懂一些计算机技术的人,那时候完全不懂的人根本不会想 到用计算机做事情。最初的需求都很具体,输入什么,做哪种计算,结果怎么输出,都讲得清清楚楚,所以最初搞需求分析的人都画数据流图,只要数据流清楚了, 软件就确定了,今天的程序员就没这么幸运了,工作流程、访问权限、用户体验等等,撞得满头都是包,如果光盯着数据流图的话,什么也做不出来。那时候的分析 员和设计师基本上是同一个人,因为没有什么好设计的,就是把功能分解一下,列张表1234写出来,再往后稍微复杂一些,所谓结构化方法,也就是功能多了一 些,列表不好使改用层次树。今天的设计师,最惨的时候 UML14 种图全都画遍,可能也还有没描述清楚的地方。
软件出现之后,因为商业的驱动,很快就泡沫一般膨胀起来,各位今天目睹了各种泡沫之后,大概会总结出来一条规律,凡是泡沫一定没有好结果。软件 一旦开始膨胀,所需的人工自然不断地加倍,于是以 IBM 为代表(IBM 确实养了不少杰出的科学家,但是养了更多猪头,当科学家和猪头一起研究问题的时候,通常猪头不会变成科学家,而科学家却会变成猪头),采用了工业时代提高 效率的不二法则--增加人手,扩大规模,精细分工,流水作业,至于结果嘛,各位学过软件工程第一课的话,恐怕就知道他们的事迹了。
扯 IBM 的糗事看似和我们的主题没多少关系,其实当中有着深刻的联系。我们前面所说的那些可以自动处理的部分,他们用人工都做得很完美,而在那些例外的地方,却几 乎无例外地犯错误。那么,里外到底是什么呢?为何总是挥之不去呢?要解决这个问题,我们就需要从更深层次挖掘软件的本质。不管怎么说,软件的核心功能就是 计算,那么计算是什么呢?今天互联网上充斥着各种各样的计算,仅仅用数学来概括是不足以涵盖其外延的。在数字系统之外,也存在各种各样的计算,比如模拟计 算机的计算,军事上的兵棋推演,商业上的决策方法等等。如果要概括所有这些计算共同的特征,就只有三点:
- 第一,都有一组初始的数据,代表着某个现实的或者抽象的系统在某一时刻的状态;
- 第二,都有一组理论或者公式(或者二者兼具),规定了各个数据如何相互作用;
- 第三,经过计算的过程,最终都得到另一组数据,描述系统在另一时刻可能的状态。
如果把第一、第二两条中的要素加在一起,称之为一个模型的话,计算就相当于模型的一次推演。模型推演是人脑最基本的思维方法,人类发明计算机来 分担思考的负担,因此计算机当然必须能够担负这样的计算工作。然而计算机并不懂得什么是模型,只是一个执行程序的机器,因此必须由人来将模型程序化,软件 简单地说就是程序化了的模型。面向对象的方法其实就是一种模型表示法,而近年更有人提出模型驱动的开发,这都与软件的模型性质密不可分。
仅仅认识到软件具有模型的性质还不够,首先,模型本身是复杂的,虽然所有的模型都可以用一组规律加一组数据来概括,但是实际做过系统的人,特别 是做行业系统的人都知道,行业知识本身就是复杂的,相互之间常常有说不清道不明的关系,如果不是自己真正理解了这些知识,仅仅以书本和专家言语为基础,做 一些表面(形式化)的推理,是几乎一定会出错的。其次,初始数据也不是简单的,今天的系统,数据来源多种多样,精度、可信度各不相同,非结构化的数据常常 见到,单是把这些数据转换到适合模型推演的形式,就要费九牛二虎之力。第三,模型代码化本身也不是件轻松的工作,今天的计算环境空前复杂,各种平台,各种 支撑系统都要考虑,今天的架构师要掌握的知识比以往任何时候都多。最后,软件虽然以模型为核心,但绝不仅仅是模型,为了让模型进行有用的工作,各种辅助系 统也必不可少。
辅助系统相对独立,我们先从这里说起。软件中最重要的辅助系统就是人机界面,人机界面到底有多重要,看看微软如何发家,以及今天微软 Google 为什么打破头就知道了,只要真正的人工智能还没有实现,人机界面就是最有利可图的领域。然而刚入行的程序员通常都不理解这一点,我自己十多年前也只喜欢编 写命令行程序和系统服务,功能复杂不要紧,只要界面少到没有就好。人机界面常常被认为是美工们的工作范围,是没有技术含量的体力活,然而事实总是无情地教 训他们,最近编 FLASH 的平均工资已经高过编 JAVA 和 C# 的了,这就足够说明问题了。程序员们之所以有这种偏见,主要是他们脑子里计算机技术装得太满,而应用场景少到几乎不存在。人机界面是软件中比较难以自动生 成的部分,特别是如果追求个性化用户体验的话。
我曾想过把用户界面都做成主视角游戏的形式,人们可以自然地走进并探索赛博空间,或许比较接近用户界面的终极形式。这当然还没实现,如果实现 了,以后老板们恐怕就再也搞不清楚员工是在工作还是在玩游戏了吧。在系统中,用户界面的作用无与伦比,首先,不管做什么计算,用户总是从用户界面得到计算 结果;其次,模型通常是反复滚动计算的,经常需要通过用户界面输入、补充或者修正数据;最后,模型未必完全由计算机实现,模型中有些部分常常不适合今天的 计算机处理,比如说图像内容识别,需要把这部分处理负担转移给人,然后人在把处理结果返回计算机,以便计算机继续计算,这样就变成了人机配合完成整个模型 的计算,这种人机结合的地方也必然需要用户界面。在软件系统的所有部分中,人机界面可能将会是程序员最后的领地。其他的辅助系统主要是人机界面以外的各种 输入输出接口,这是最容易实现自动编程的领域,无需细说。
数据源的问题也相对简单,而且有 TBL 等牛人一直在推动数据标准化和互操作,这件事情看起来是无需我们操心了。如果他们最终成功(我毫不怀疑这一点),最终任何数据都可以按对象组织起来,并且 得到一个人类能看懂的标签,而且标签的编写方法符合严格的形式化定义,我们只要等着到时候从 w3c 下载解释程序就足够了。至于这些对象该放到系统里的什么地方去,那就与他们无关了,是构造模型的人考虑的事情。
模型代码化不仅取决于模型本身,更受计算环境的制约,这是绝大多数程序员所认可的“纯技术”活,需要调动程序员最多的关于计算机系统的知识来完 成。模型代码化的工作包括写出使计算机可以完成模型运算的代码,以及把模型与周边辅助系统衔接在一起的代码。高手和软件厂商通常都会编写一些程序框架,以 便抹去计算环境不同带来的复杂性,让程序员专心处理模型,语言虚拟机,应用服务器都属于这一类。模型中有一些功能是比较容易自动编程的,比如各个对象的属 性定义和 CRUD 方法,这部分代码的自动生成今天已经基本实现了。至于模型规则的代码化,这个麻烦可就大了,要先解决了模型本身的复杂度才行。
构造模型是人脑的一个基本功能,所以我们常常觉得这很容易。然而一旦交给计算机,其中的复杂性就显现出来了。人脑中最简单的模型是场景模型,也 就是所谓的形象思维,具像思维。这是每个人在小时候,能够使用语言进行抽象思维之前,唯一可用的思考模型,有一定思维能力的高级动物也具有使用场景模型的 能力。场景模型是最常用的思考模型,在其他模型无法解决问题的时候,场景模型总是作为最后的手段。场景模型的推演是基于经验的,因此只要能够构造出来,就 总是能够有效地完成推演,而不必担心没有理论可用。然而场景模型并不简单,世界上对场景模型认识最深刻的人群莫过于影视导演了,他们几十年的功力都花在营 造让大多数人感到真实可信的场景上了,只要看看成为高水平的大导演的难度,就知道全面认识场景模型有多困难。
今天的计算机系统是不具备像人类这样的场景推演能力的,不过人工视觉的研究近年来进展很快,其中相当大的一部分就是解决计算机对视觉场景自动建 模的问题,而视觉又是人类获得场景信息的主要信息源,可以说只要解决了视觉场景的建模,机器理解场景就至少成功了一半。这方面我乐观一点估计,20年后技 术基本就成熟了。比场景模型高级一点的是语言逻辑模型,这种模型的理论都是用语言表示的,模型本身也都可以用语言精确描述出来,相比之下,场景模型虽然也 可以用语言来描述,但是很难做到完全不丢失和歪曲信息,特别是当其中有些物体无法对应到被人们广泛熟知的概念上的时候。语言逻辑模型的推演就是我们平常所 说的逻辑推理,也就是用语言形成一条逻辑因果链的过程。这类模型因为本身就是形式化的,能用语言外在地表达,而且较少模糊与歧义,因此传统人工智能领域研 究得比较深入透彻,剩下的工作主要是与其他模型如何结合的问题。如果一个问题可以建立语言逻辑模型,那么一定比针对这同一个问题所建立起来的场景模型运算 量小很多,这就是抽象的优势,因此效率大大提高了。
在场景模型和语言逻辑模型基础上,人脑发展出了称为“数学”的东西,这是更高级的模型系统,具有更高的推演效率,心理学家把感官信号称为第一信 号系统,这个系统对应着场景模型,把语言称为第二信号系统,这个系统对应语言逻辑模型,照此推理,数学应该称为第三信号系统才对。数学因为具有逻辑模型的 抽象特点,因此很多数学问题可以形式化,非常适合计算机处理,然而,因为数学又有一些部分以场景模型为基础,所以也有一些数学问题很难用计算机处理,这些 不好处理的特例,恐怕要等计算机处理场景模型成熟起来之后才有望解决,我认为30年是个合理的预期。
在解决了模型自动构造的基础上,就有希望创造出具有人类思维能力的计算机系统,然而,计算机比人脑还缺一样重要的东西,我们前面说过,计算机没 有主观能动性,因此处理信息的工作需要人来驱动。如果要象人脑那样完成复杂的工作,计算机必须要自我驱动。今天的计算机有时候也能完成很复杂的计算任务, 但是这是以软件复杂性的极度增加为代价的,而这增加的复杂性,其实所做的只不过是把启动程序时人类赋予的那个初始驱动力,不断的转换成各种形式,传递给各 个计算单元而已。人类这种神奇的初始驱动力,来源于自身的生命力,或者说具体点,来源于人脑的情感欲望系统。
生命力并不是什么神奇的东西,动物也都一样具有,只不过地球上的人类以外的动物还都没有聪明到能够学会计算机,所以它们不能驱动计算机帮他们做 事情。情感欲望在计算中所起的作用,至今一直都被学界忽视了,这个系统看似与理性无关,却是产生智能所需的核心部件,人工智能至今没有实现,恐怕和科学家 们还没有想到这一点大有关系。再说句题外话,这恐怕和研究信息技术的以男性为主大有关系,男性大多在情感上迟钝,欲望又简单直白,没有深度,所以想不到这 其中的关联也在情理之中。未来的智能计算架构,应当是一大群计算单元,具备各种模型处理能力,在一个模拟人类情感欲望驱动机制的核心的驱动之下,不断碰撞 组合,相互竞争,优胜的计算单元获得更多资源和信任,从而推动整个计算体系不断进化,最终产生出智能。50年后,在我离开这个世界之前,或许能够看到这样 的系统最终成熟起来。
模型的自动组合,其实就是软件的自动组合,在有了这样的系统之后,任何软件都能自动组合出来,等到那一天,最后一位人类程序员就终于可以退休了。