作为一名由业余编程成长起来的程序员,我已经有二十多年的写程序的历史。
小学二年级时,爸妈给我买了一台二手的 IBM 8088(虽然当然 Intel 已经出了80486,但其价格远远超过国人普遍家庭能负担的程度),很快我就参加了选择并不多的市少年宫学习电脑的使用。当年少年宫的电脑大概应该是 Apple II,或者是山寨的兼容机,没有软驱,只能学习自带的行号 Basic。可惜当时老师的水平也有限,所以学的东西也都是行号+goto,并没有结构化的设计。后来快到6年级的时候,老师打算教 pascal 语言,然而老爸觉得再学下去意义也不大,于是就不再上了。大概因为我在班上的学习成绩最好,听说我不上了之后,其他同学的家长一合计也都陆续退出了。96年我上初二的时候,父母一半也算是为了奖励我,花了小一万给我买了一台 P133 + 16M RAM + 1G Hard Disk。当然那个时候选择已经很多,炎龙2、仙剑,各种游戏玩得不亦乐乎。一到假期不是打游戏,就是看小说,估计也是因此眼睛很快就用坏了,现在截着700度的眼镜。因为有了行号 basic 的底子,所以一开始自己买了 VB 的书。瞎弄了几年之后,觉得是 VB 这语言不行,开始买 Delphi 的书。可以说,那个时候就开始没有老师教,拿着书自学。搞了好多年,然而也折腾不出来个所以然来。
高三下学期毅然封掉了电脑,疯狂学习了半年,最终如愿进入了大学。由于大学期间感情生活并不顺利,开始了更加疯狂的游戏生活,基本上就是 CS1.5 + Warcraft3。直到大四的时候,学分已经修完了,一边迷迷糊糊的找工作,一边开始研究魔兽3的自定地图。那个时候还没什么人玩 dota,国内最火的是各种 3C,比如浩方上流行什么澄海之类的,而在教育网里,天朝最好的大学最爱玩的是 C3C。我们一帮玩家就总结过,难度越高的 3C 玩的人越少,C3C 算是最简单的,30满级,固定阵容,最受欢迎。F3C 最高10级,每个英雄上场机会都很高,各种钻洞节奏极快,对配合的要求非常高,大概有30多个核心玩家,每天晚上都能打几盘。E3C 同样10级满级,难度最高,前几级英雄基本单挑不过小兵,攻击差了好多好多,一个小兵没血了经常都是几个技能一起丢过去,以致于后来玩 dota 的时候感觉补刀简直就是小儿科,基本上一个月只能凑出来两三局。再往后 moba 类游戏的发展,也都是越来越容易上手,难度越来越低。
既然是高智商玩家,大家的动手能力自然也很强,很多人都打算自己也做一个玩玩。然而暴雪提供的工具并不够好,如果不用图形界面而是直接写代码的话,编辑器没有高亮也就算了,还时不时容易崩溃。在试遍了当时各种方案之后,并没有我十分满意的工具,所以就打算自己写一个。大四本来也没啥事儿,核心玩家也各奔东西能凑上的局越来越少(我的电脑后来不太能撑得住 C3C 所以基本只玩 F3C),花了大半年时间,终于在05年的时候发布了正式版。后来又辛辛苦苦找代理发布在国外的 war3 地图论坛上(不知道现在什么情况,我们那时候教育网只能免费访问国内网,出国是要找代理的,不然费用高得吓人)。可以说这个经历是一个转折点,只是为了让代码工作,各种请教玩家中的高手,硬着头皮啃英文帮助文档。在那之前,做不出来的功能就觉得是编程语言不行,换个语言就有办法了,后来才知道是我使用的姿势不对。大家都是用着同一套 win32 api,只不过控件的封装方式不太一样而已。同时也从各种 VCL 控件的源代码里学习了大量知识,包括许多以前完全想象不到的技巧,一直维护到2005下半年。等后来再看代码的时候,就已经觉得刚开始的时候写的代码惨不忍睹,只是为了让功能工作,完全不是正常代码应该实现的方式。
Jass Shop Pro 也在那几年成了 war3 开发的标配,成为了世界上最受欢迎的 Jass 编辑器。然而程序偶尔也会跳出来一个内存地址出错的提示,以当时的我的水平还找不到原因为啥会出那个 bug。还有个有意思的事,Ice Frog 也就是 dota 的作者之一,当年还给我发过邮件,询问软件的更新计划,还鼓励我坚持下去。话说我当时也不玩 dota,突然来了这么一封姿态有点儿高的邮件,还觉得有点儿蒙逼你谁啊,加上英文也不好就随便回了一封。多年之后打算清理一下邮箱的时候再一次蒙逼了,当初我要是能抱上 ice frog 的大腿那岂不是已经发达了?
不过这次经历让我清楚认识到自己能力的不足,好在 jass 的语法简单,我楞是野路子写出来了一个 parser。当时看到那些语法高亮编辑器控件的代码之后,我已经是相当震惊了。当我知道这是科班出身的标准实现时,就开始狂补功课。算法和数据结构基本上是挨个用 Delphi 实现了一遍,龙书也啃了大半年,也靠着 delphi 的调试器把 Intel x86 保护模式语法给学会了。Windows 核心编程也反复看过几遍,后来读了张银奎的《软件调试》,大概才算是出山了。然后就开始啃 delphi 编译器的实现,什么 RTL、VCL 啊都看过好多遍。可以说 Delphi 是我会的这么多语言中,唯一一门我花了多年时间研究成能够称为专家级水平的。
一个有意思的现象是,科班出身的多数人在学习概论性质的专业课时,并不能很好的理解这门课解决的是什么问题。而我在努力去补计算机专业知识的时候,是带着问题去学习的,所以经常碰到一些东西的时候就非常开心:原来我以前遇到的那个问题可以用这个方法解决啊,实在是很妙啊。这也是高等教育很尴尬的一个地方,概论类的课程,往往是从一个高屋建瓴的角度,去讲这个专业上会遇到哪些非常本质的问题,以及有哪些思路、做过哪些探索试图去解决这些问题。但是当啥也不懂的小朋友学这门课程的时候,往往是鸭子听雷,讲这么多废话有毛用?所以尽管我并非编程科班出身,你让我说设计个简单电路门我肯定不行,但一直带着问题在编程语言上的潜心钻研,让我非常有信心在基础上不会输给任何人。可以说在我打下坚实的基础之前,写出来的代码只能算是小儿科,只知其然不知其所以然。而这之后,对于很多程序的实现,有了一种自然而然的感觉,能够猜出来背后的实现方式——当然不包括算法方面的东西。
顺便说一下,编程这个东西其实是很简单的机械思维,虽然跟自然语言差异很大,但本质上一是一二是二,不会有任何复杂的东西。但我的意思可不是理论没用啊,虽然现在新技术层出不穷,但如果你搜一下理论基础的话,基本上都是80年代以前就已经有人提出来过了,很少有真的很新的东西。所以咱们今天吃的大多都是爷爷辈的剩饭,新鲜的东西其实并不多。
有了 Delphi 的底子之后,我再学任何的语言都是非常的轻松了。我接触过的编程语言(指有控制流的,XML、CSS 这种自称是编辑语言的不算),至少是写过几段有功能的代码的(hello world 不算)、可以不脸红说我过的包括,正经做过东西的:Delphi、JavaScript、Ruby、PHP、CoffeeScript、Java、Python、Kotlin、Swift、PL/SQL(Oracle存储过程语言);玩票性质的:行号Basic、VB、C、C#、Go、D、Perl、Objective-C、x86汇编;勉强能凑合用的:C++。大概就是瞅过语法记得几处有意思地方的:Rust、Elm、Lisp,还碰过一些其它杂七杂八的语言,一时半会儿也想不起来。
所以这个系列的杂谈,是建立在语言发展历程的角度上,谈一谈我个人对编程语言的算法。换句话说,就好像科学史或者科学哲学之于科学一样,泛泛的谈一谈我对编程语言发展的看法,或者说,在时代背景上,一门语言的出现解决了哪些编程中遇到的问题。希望各位能够在读过我的文章之后,学习一门新编程语言不再觉得是一个负担,同时能够理解和吸收新语法,写出更加健壮的、符合新的理念的代码。