文/苏晓路
某天看到一篇博文,《一百年后,人类怎样编程?》,只是这个题目,就勾起心中无限感慨。文章没细看,内容大致是分析各种语言,以及其中各种语言现象,今后的发展趋势。我对于语言的进步一直不感冒,对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年后,在我离开这个世界之前,或许能够看到这样的系统最终成熟起来。
模型的自动组合,其实就是软件的自动组合,在有了这样的系统之后,任何软件都能自动组合出来,等到那一天,最后一位人类程序员就终于可以退休了。