编程观:从黑客帝国谈起

    在电影黑客帝国中,网络黑客尼奥发现自己身处的看似正常的世界实际上被某种力量控制。他在网络上探求真相时,遇到了黑客组织的崔妮蒂和组织首领墨菲斯,得知他身处的世界实际上是由一个名为“矩阵”的人工智能系统构造的虚拟世界,而现实中的每个人则被豢养在培养液中,只是意识被接入到了虚拟世界。尼奥从此走上了抗争“矩阵”的道路。

编程观:从黑客帝国谈起
黑客帝国1海报

    黑客帝国1于1999上映,是一部经典的科幻片。其中,关于我们身处的现实是否是虚拟的思考,充满着浓厚的哲学意味。特别是,我们身处的现实可能由程序模拟的思想,从某种角度讲,展现了计算机编程具有的独特特性。如果有模拟城市、虚拟人生等类似游戏的体验,或许能够理解这中间所包含的看似荒诞、实则发人深思的假设。     同样类似的情节出现在哲学启蒙性书籍《苏菲的世界》这本书中。但书中情节与电影黑客帝国不同是,《苏菲的世界》提出的一种假设是我们身处的世界可能是某本书中描绘的内容,这种假设本身也是一种不可证伪的观点。好在这里并不是为了讨论哲学命题,而是想要探求为什么会有不同载体的虚拟世界和这些假设的基础。
编程观:从黑客帝国谈起
苏菲的世界中文版封面

    《苏菲的世界》认为世界可能是由文字所描述的,而电影黑客帝国假设世界可以由计算机程序模拟。这是因为无论是文字还是程序都具有一个相同的属性--抽象。所谓抽象可以简单理解为描述世界、模拟世界的能力。在描述或者模拟的过程中,也会存在着简化和提炼。同样的,我们学习过的各种学科,包括数学、物理、生物等等,也是将现实世界中的事物或者问题抽象简化为数学表达式或者物理公式,最终经过假设和推演获得事物的状态、变化的结果或者说问题的解。从这种角度讲,我们的知识都是对世界的认知,本身就是抽象后的结果。

    比较朴素的编程思想其实和数学、物理学科的基本思想是一致的,即认为世界可以由某一时刻的状态和状态改变的操作所描述。举个比较浅显的例子:小明有1个糖,吃掉1个,还剩几个?这个问题,“某一时刻的状态"是“有1个糖”,状态改变的操作是“吃掉1个”,那么就能够推导出执行“状态改变操作”之后所处的状态是"0个糖”。这个例子虽然简单,但是需要知道复杂问题其实也是由简单思想有逻辑的结合在一起来解决的。上述例子可以引申出新的问题,小明有100个糖,每1小时吃掉一个,43个小时后还剩多少个?(对,小明就是一个没有感情的吃糖机器)这个问题如果从数学思维讲,就是简单的100-43.但是从对现实世界计时的角度来说,就包含了状态的随时间的连续变化,一个状态通过状态的改变达到一个新的状态,然后新状态作为新的起始状态继续通过-1的状态变化达到一个新的状态,直到43小时这个问题的终结。综合上述,这就是面向过程编程的基本思想。其中某一时刻的状态能够由数据来描述,而状态改变的操作即为算法。

编程观:从黑客帝国谈起
电子跃迁:状态改变--实现电子从一个状态到另一个状态的变化

    面向过程编程面临的基本问题是分类与分步。     分类需要处理状态维度和状态改变的操作维度的问题。由于整个流程中状态和状态变化操作的不确定性,需要对新状态的可能结果进行分类,而接下来可又能由于新状态的不同,对应的状态改变的操作也不同,所以也需要对状态的操作也进行分类。分类思想体现在编程语言的if else基本语句,这也是为什么无论编程语言怎样变化,if else基本语法永远会存在的原因。     分步就相对没有那么复杂,每一个状态通过状态改变到达新状态的过程的粒度,就是分步需要解决的问题。比如100个糖的问题,最直接的数学思想100-43,即100是初始状态,状态的变化的操作是“吃了43个小时”,所以新状态是57。我在举例时介绍的情景想要要表达的是用更细的分步粒度,简单表述是100-1-1···-1,每一小时减去1,连续减43小时。看上去在这种情景下,这种细粒度的算法像是初学数学的小学生扳指头算数,但是在一些复杂情景下,如何切分算法的分步的粒度,是一个很高深的学问。在编程中,顺序语句和循环就对应着分步的思想。

    面向对象编程并不像面向过程编程那样容易理解。电影黑客帝国中,有一个情节是说指导尼奥和黑客组织的精神导师"先知"实际上只是一段程序。在“矩阵”模拟的世界中,存在许多没有现实实体、只是人工智能编写的程序的“虚拟人”,这里把这些人称为有思想的(游戏中的)NPC可能更容易被理解。不止有人,“矩阵”世界所有的物体都是程序的模拟,包括高楼大厦、食用的牛肉红酒等。

编程观:从黑客帝国谈起
黑客帝国中用来表现世界是由程序模拟的剧照

    面向对象编程一部分可以理解为将一些实体的概念封装成了类。比如,可以将人类认为是具有“人”的共通属性的类,而每个具体的人就是这个类”实例化“的对象。通用的属性对应到每个具体“人”的对象身上,就是用来代表某种人的状态,这个状态可以是静态的、不随时间变化的,也可以是动态的、可以通过状态操作改变的。类中不仅有状态,还有状态改变的操作--类自身定义的方法。根据这两个特征,其实可以察觉出面向对象编程似乎只是面向过程编程的一个变体。面向过程编程中,所有的状态数据的有效区间只能是全局的或者函数内部的。但是面向对象编程实现了一种中间态,即既不是全局的能被所有函数访问的,也不是函数内部仅供函数自身访问的,而是可以实现在类的内部多个函数间进行状态共享。这种封装可以认为是在面向过程编程的基础上进行了模块化改造,每个类都是一个"子系统",“子系统”和全局、"子系统"和“子系统”可以通过外部方法的接口形式进行交互,子系统的内部又实现了局部状态的变化。     既然是面向过程编程的变体,面向对象编程同样需要解决基本问题分类与分步。当然,在面向对象编程过程中,仍然可以使用面向过程编程的分类和分布的思想,很多时候也逃不开使用它们解决问题。但是,更好的解决方法是使用面向对象编程的分类分步思想,这一部分知识主要就是设计模式想要解决的问题。

思索

    面向过程编程中目前普遍使用的分类,都是基于一个“有限”的前提,即状态不会无限多,状态对应的操作也不会无限多。在分步这个维度,可以构造“假”死循环来实现形式上的类“无限”,很多web服务器也是这样实现的。但是在分类维度,if else的分类总是有限的。有人可能会反驳说,我们不可能预想出所有的情景,特别是无穷又该如何定义?我这里之所以考虑这个点,是从程序智能的角度思考,如果在编程语言底层实现上能够允许程序的自我编写,比如自行实现情景预测,编写自己if else逻辑,这是否就算作程序具有了学习能力,能够实现真正的人工智能。