本帖最后由 蓬草 于 2020-12-14 13:29 编辑
流年往事:门外汉的编程梦
蓬草 写于2019年6月下旬
从上世纪⼋十年代起,我开始自学编写计算机程序(简称编程)。这三十多年里,为此不知花过多少⽇日夜夜,自认可以称得上是个业余程序员。这些年里,所写的程序不下⼗几万⾏,全部打印出来要有两千多页,超过我参与写作的科学论文厚度一倍有余。
⼀见钟情
大学毕业时,我考取了河南医学院神经⽣理专业的硕士研究生。其后⼏年就学习做睡眠⽣理⽅面的研究。因为要和电⼦仪器打交道,我们⽣理学专业的研究⽣都要学习一点电子学的知识,顺便了解一点⼉计算机的常识。有一年夏天,我师兄说郑州大学有关于计算机程序的讲座。于是我们就一起去听了一次,结果什么也没听懂。不过这倒激发了我的兴趣, 回来后就买了一本BASIC语⾔的教程。然后是连续⼏天阅读,基本上弄明白了其中的道理。可惜因为没有计算机,⽆法进⾏演练,只能纸上谈兵。
1986年,我到了到美国,在纽约市⽴大学修实验⼼理学博士学位。我的导师有一台IBM的8088计算机,这在当时算得上是先进武器。我看着十分眼馋,惦记着一定要自己买一台计算机。一年多以后,我攒够了钱,立即花了 2700美元买了一台80286型计算机。那时纽约市⾥有许多卖计算机的小店,离我住处不足二百米远就有一个,其老板是个中国⼈。我兴高采烈地将计算机从他那里抱回家,开箱后连线,启动成功。随后我在键盘上敲进刚学会的几个DOS 指令,运⾏无误。于是我就开始规划,建立新的目录 (directory,现在叫作⽂件夹),准备把不同的文件安排得井井有条。很快,我看着根目录里面的⼏个⽂件很不顺眼。 这⼏个家伙也不知是何方神圣,居然堂而皇之地赖在根目录的大厅里。无名小卒怎可⾼居庙堂之上? 不如将其放逐,驱赶到一个犄角旮旯的小房间去。 它们也真驯服,在我的指令下乖乖地搬家。可等我关机后再次启动时,屏幕一⽚漆黑,不知出了了什么问题。 于是我赶紧挂电话找销售商抱怨: 刚买的计算机,还不到三小时,怎么就不工作了? 接 电话的是位中国姑娘。 她问明情况后,与另外一位姑娘对话⼏句。接着,我在电话里听到一阵笑声。笑声过后,她们向我讲明原委, 根目录里的几个文件是操作系统的启动文件。它们被我驱逐后,计算机就找不到这几位大神了,除瘫痪之外别无选择。 于是我按照电话里的指导,⽤系统软盘启动计算机,然后不惜前倨后恭,又把那⼏个大神请回庙堂之上。 解决问题之后,悬着的心终于落下。第二天, 到学校向导师⼩小炫耀了一把。从功能上说, 我的80286碾压导师的8088是没有悬念的。当然,我们中国⼈一向谦虚谨慎,驱逐“大神”的壮举就隐下不提了。
⼀机在手,便想开始学习计算机语言。到书店搜寻,见到许多关于计算机语言的入门书籍。我在其中浏览,发现C语言是⼀门功能十分强大的计算机语言。 于是挑选了⼀本书,拿回来自学。很幸运的是,这是一本很好的入门书籍,讲解畅晓明白又不失深度。其中有很多短⼩精炼的示范程序, 十分便于初学者拿来演练。我很快就喜欢上了这门语言,不再去惦记BASIC语言。C语⾔不仅功能很强大,其语句也十分简炼。例如,⼀个变量量的增减,只须在其后打入 “++”或“ - -”,就可自动完成。这个风格也被其后的C++ (读:C加加),JAVA和C#(读:C sharp)等语言所继承。而C++语⾔正如其名称所提示的,是C语言的一个进化,是在其基础之上延伸出来的一个扩展集。 时至今日,人类发明的计算机语言不下几百种。如果以英文的26 个字母作为计算机语⾔的姓,那么每个姓后面都跟随着几种至 ⼏十种语言。⽽C/C++语⾔从问世以来,一直都在应⽤的广泛性上占据头⼏把交椅。我也很幸运地选择了它们为我的编程工具。
难以自拔
⼋十年代,正是神经网络研究兴起的年代。其主要方向是研究如何用计算机模拟脑的认知过程,如感觉、知觉、学习、记忆、 运动控制和语言学习等⽅面的课题。其共同的特征就是采⽤大量类似神经元的简单运算单位,形成网络,做平⾏分布式信息加工。我们系里的⼀位教授也不失时机地开了一门这⽅面的课程:认知的连结主义模型(Connectionist model of cognition)。我最初学习编程的一个动⼒就是想了解如何用计算机去模拟人脑的工作方式,以便对于⼼理问题有更深⼊的理解。在学习C语言的同时,我⼜买了不少有关计算神经科学的书籍,其中有一本书很有趣。与当时绝⼤多数的神经网络模型不同,其中模拟的计算单位比较接近神经元的真实情况,考虑到神经元膜电位和突触电位的动态变化。对于有医学和神经科学背景知识的人来说,这无疑更有吸引力。 不仅如此,这本书⾥还有许多用FORTRAN语言编写的程序。FORTRAN(FORTRAN是 Formula Translation的缩写,意为公式翻译),是⼀种比C语言资格更老的计算机语言,被广泛用于科学和工程计算。相比之下,C语言更先进, 更强大,更灵活。有了C语言的基础,用FORTRAN语言写的程序也能猜懂。我不禁见猎心喜,还将其中的⼏个程序用C语言改写了一遍。
我很快发现,这是⼀个知其不可而为之的事情。其中一个主要的障碍是我的数学基础不够。尽管上大学时自学过高等数学,还是不能够应付较高深的问题。 奋蚁躯而负泰山,⼼有余而⼒不足。另⼀个显⽽易见的事实是,计算机的内存和运算能⼒远远比不不上人脑。采⽤这种比较接近神经元细节的模型需要更多的内存,⽽我的80286电脑最多只能模拟几百个经过相当简化的神经元。不仅如此,这种模型计算起来还很费时间,我让计算机运⾏了一夜,也不过模拟了这片网络⼏秒钟的活动。面对巨⼤的反差, 顿感⽆比失落。几百个神经元,模拟运算⽐真正的神经元慢了⼏个数量级, 这也配称为电脑? 因为这个原因,我一直都不愿意使用电脑这个词。其实当年我陷入困境的更深层原因是,我对计算机模拟的理解有误。 如果采用其它更简单的模型,当时的286计算机仍然能够做一些令⼈惊诧的神经网络研究,运算速度也会提⾼几个数量级。 面对现实,我只好放弃向这个方向发展。尽管如此,日后许多年里,我还是时常按照摩尔定律推想什么时候电脑的内存可以超越人脑的容量,运算能⼒可以超越人脑。时至今日,⼈⼯智能的发展已经在许多⽅面超出我的想象。 为此, 必须对那些在此领域耕耘的科学家及程序员们脱帽致敬。 那些大部头的神经网络著作早已成了书架上的艺术品,我有闲暇时便拿下来把玩一会儿,回忆尘封的往事和当年的梦想。 理解大脑工作的秘密,永远是神经科学研究者的一个梦想。
在我博⼠毕业前一年的时候,我编程的兴趣突然有了更现实的理由。有一天,导师对我说,找不到钱了,给你的经济资助只剩下不到一年的时间。读博⼠期间,我一直是每周用 20小时为系⾥面养实验动物,作为获取经济资助的方式。前⾯几年,每年有一万美元的资助。到了第五年,由于学校财政紧张,每个⽉是七百美元。在纽约市生活,每⽉七百美元已经是捉襟见肘了,如果再失去这七百美元可不是小事。 我的博⼠论文是研究睡眠的两性差异,⽤大鼠做实验。这项研究的工作量很大,只能不分昼夜的干。那时睡得很少,尤其是最后⼀年,每天只能睡3-4个小时。其中⼀个重要原因是分析睡眠资料太费时间,那时的睡眠资料是⽤纸纪录,⼀个⼩格⼀格⼩格地算。因记录时间太长,只能以最慢的纸速记录。脑电图记录纸上的每⼀个小格子是7.2 秒,每页是50个小格,相当于360秒,1小时用10页,24小时⽤240页。这样,一本500⻚的脑电图纸可以记录两昼夜 的睡眠资料。可是每只动物的记录时间是几天(雄性⼤鼠)到⼆十几天(雌性⼤鼠,需要监测⼏个性周期)。每天同时记录8只动物的睡眠,记录速度远超分析速度。我计算照这样下去,需要两年半的时间才能分析完实验资料,还要加上书写论文的时间。这样, 一年后就要到校外去打工。可是这样一来,每天还要花更多的时间在往返交通上面。 情急之下,我想到了通过写程序解决我的困境。我决定用键盘敲数字代替数格子,用1、2、3分别代表觉醒、非快眼动睡眠和快眼动睡眠。十个数字一串,每⾏分割成5串数字,合起来代表6分钟的资料,恰好相当于⼀页脑电图。 数字输入后,由程序自动计算,避免了数格子的麻烦和⼈工计算的错误。这个简单的小程序把资料输入和计算速度提高了几倍。 导师⼀⻅也大为欢喜,这样他就不必再为我找经济资助发愁了。这是编程第一次帮助我应付了⼈生中的重要难题。 这个经历令我相信,好的计算机软件是提高工作效率的有⼒工具。
博⼠毕业后,在工作中应用编程的机会就更多了。甚至可以说,我日后工作中的最主要手段是通过编程来解决很多问题。 1991年年,我到了了田纳西州⽴大学医学院做博士后,继续基础睡眠研究。 其后不久我开发出了自动记录睡眠软件和相应的分析系统。这个软件在实验室里应用了⼗几年,也流传到另外几个实验室。到了世纪之交,它又传到了中国。 我通过远程指导,帮助当时中国睡眠研究会会长在其实验室建⽴了自动记录系统。 十五六年后,我们终于在⼀次学术会议中首次⻅面。直到⼗几年前,我所写的软件都是在中美⼏个实验室里免费使用。至今,我还有一个小程序供国内实验室免费使用。能够为中国的睡眠研究有一点⼩小的贡献,也是一件快事。
从2006年起,我逐渐意识到知识产权问题,开始将一个新款软件商业化,并向另一个小公司兼合作者提供少许有偿服 务。凡夫俗子,做不到“空即是⾊,色即是空”,为了生存总要应付皮囊之事。假如能够由此获得经济上的自由,不再为生计操⼼也是一个诱惑。 在美国生活多年,也确实经历过⼏段软囊羞涩,不时为孔方兄发愁的⽇子。可惜经商需要另⼀⽅面的才能,非我所长。 睡眠基础研究是一个非常⼩的研究领域,与此相应的市场也极小。 受研究经费所限, 研究者们(包括我⾃⼰在内)在购买软件时都很“吝啬”。此 外,软件⼜是个⽤不坏的产品。购买⼀次,使⽤⼗几年不成 问题。 因为这几个原因,经济自由的想象也成了望梅止渴。业余程序员总是不及职业程序员;回报总是不及劳动付出。 不 过,有收⼊毕竟聊胜于无。
除了以上⼏个显⽽易见的动⼒之外, 还有其它因素使我对编程难以忘怀。这⼜不能不说说编程的甘苦。
无穷苦恋
因为是自学编程,也就有许多短板,需要不时充电。为了实现梦想,我花了不少钱买各种编程的书籍。这对于读学位的穷学生来说也是一笔可观的支出。为了节省买书的费用,我参加了⼀个计算机图书俱乐部,买书可以得到优惠。 尽管如此,那段时间里在经济上还是捉襟⻅肘。博士毕业后,经济状况逐步改善。 这时的花费则主要是不时购买新的计算机,以扩展其计算和存储能力。 到目前为止,大概买过十五六台计算机了。除此之外,还要购买新的编程工具,以适应从DOS操作系统到Windows操作系统的转变以及 Windows的升级。与此同步,购买编程工具也是必要的花费。有时候,人放不下一件事,不一定是因为这件事情有多么了不起,而是因为在它身上投入太多。编程对我来说也是如此。感情压倒理智的时候很多,同甘共苦的经历是不大容易舍弃的。
计算机软件的发展日新月异,软件工具也是如此。但是如何维持向下兼容也就成了一个问题。为此,保留旧的计算机和原有的编程软件就很重要。 我的一台手提电脑已经应用⼗多年,老旧不堪:电池换了两次,已经买不到可用的;联⽹的网卡早已失灵,倒也⽆无妨,免掉许多杀毒软件及各种烦恼;数⽉前显示屏也拒绝⼯作,只能用外接显示屏。 这一切都令我担心, 因为我最重要的编程工作都在这台电脑上做,而旧的编程工具⼜⽆无法安装在新的电脑之上。 为应付这个问题,我特地从eBay上面花48美元买了一台旧手提电脑。 可是一试,外接电源和电池都已经寿终正寝,还得再换一个。 原来开发这个编程工具的Borland公司早已消逝多年。 比较麻烦的是,原始的编程软件还在我的旧电脑上面,但是原始价值⼀千美元软件盘已经破损。感谢eBay,数日前⼜在那⾥花20美元买到当年(2002年)价值3000美元的企业版编程工具,C++ Builder 6。虽然老旧,但依然功能强大,基本上能满足我目前的需要。 ⽽同一系列中的最新版本却不甚合意,其中绝大部分的新功能对我来说都用不上。
除了这些琐碎的烦恼,程序员每天都会经历的一个苦恼是除错。 每个程序员都会犯错误, 何况对于门外汉和新手?三⼗多年里,也不知花了多少日夜去纠错。 我的博⼠导师也喜欢⽤计算机处理数据,他主要是应⽤大学中⼼的主机,需要⽤一种特殊的语⾔编写一些指令,也时常被计算机程序中的错误所折磨。有一天他对我说,真想把计算机杀了。对此,我深有同感。
在上世纪⼋十年代和九十年代初期,计算机的内存都很小。 只有640K字节(1K相当于2的10次方,1024个字节)。去掉操作系统所占的空间, 留给程序员可用的内存只剩下500多K字节。 如何在这个很⼩的空间里进行⼤量数据的演算,是⼀个很费脑筋的事情,需要施展一番辗转腾挪的功夫。C语⾔及其扩展语言C++在这⽅面有很⼤的灵活性,可以动态地分配内存的占用。同时它又可以用指针去指向不同的内存空间, 进⾏快速的运算。但是这种指针运算⼜是最容易出错和最危险的地方,以至于一些新的语言如Java和C#都坚持不用指针。尽管有这种缺点,C和C++语言,仍然是软件业不可或缺的工具,并得以广泛应用。 我所遇到的麻烦大多和指针的错误有关。每逢遇见它就要冥思苦想,判断在哪个环节出了问题,然后修改程序,反复试验。程序出了错误或漏洞,就要除错。两年前,我曾经写过⼀首 小诗(编程 - 除错),这样描写了一次除错的经历:
欲解新题思不穷,连番妙算反成空。 谁能毁我囊中策,此夜无眠捉⼩虫。
诗中的“⼩虫”是指计算机程序中的错误;“捉⼩虫”代指纠错。早期电⼦计算机的运算依赖机械接触的继电器。1947年,在哈佛大学工作的Admiril Grace Hopper⽤马克-2 (Mark II)型计算机运⾏一个程序时出现故障。她的助⼿发现有⼀只飞蛾落⼊一个继电器的接点之间,由此阻碍了计算操作。后来,计算机程序中的毛病就称为⾍子(bug), 程序除错的英⽂原⽂就是“去掉⾍子”(debug)。除错⼀词的翻译保留了精确的技术含义,但却失去了原⽂的生动。 技术领域的语言比较平实,翻译比较容易。但偶尔⼀涉及到文化,翻译立即就变得笨拙,没法传达其中的韵味儿。其实Hopper后来承认,她并没有创造出debug这个词。早在1878年,托马斯·爱迪生就把机械中的缺陷称为bug。 1944年,奥本海默在组织制造原子弹的“曼哈顿计划”时,也曾经用过“debug”⼀词。⽽英国航空协会也曾于1945年用 “debug”描述飞机发动机的问题。科技进步离不开除错,但是debug这个词能够得以普及,却是要感谢那只制造麻烦的小飞蛾。
除错是计算机软件开发过程中非常重要的环节。一般来说, 系统越复杂,错误就越多,除错越困难。麻烦往往从意想不到的地方和时机冒出来。小毛病仅碍观瞻,致命大患则可以使程序突然中⽌运⾏或发生癫狂。出了⼤毛病,当然不能如我导师所形容的那样,把电脑杀死。 此刻,最好能表现出一点点⼉大将风度:猝然加之⽽不怒,泰⼭崩于前⽽面色不改。重要的是头脑冷静,思维缜密。这样才能谋划各种应对措施, 捉住捣乱的⼩虫。这是⼀个考验脑⼒、体⼒和毅⼒的过程。
探索未知
临床应⽤的脑电记录设备都带有相应的软件,可是动物实验所需记录的商业软件却很少而且不太好用。这是由于基础睡眠研究是⼀个很小的领域,研究者的科研资⾦⼜很有限。 极⼩的市场完全应付不了软件开发的成本。商家大都不愿意做这种亏本的生意。 做博⼠后的第一年, 我发现实验室⾥用的软件不好⽤,分析效率⽐我的原始软件的效率还低一倍, 而且分析出错时还不能随时改正。当时我们实验室⾥有5-6 个⼈做动物的睡眠实验,每⼈每天要花70-80%的时间去分析原始资料。如果能够将数据分析的效率提⾼⼀倍,实验室就相当于多了两个研究者。于是我悄悄动手,开发自己的软件,解决从数据记录到数据分析的⼀系列问题。这个事件好⽐瘾君子爱上了潘多拉的盒⼦,带来了无穷的烦恼。当然也有编程得⼿后的快感,就算是吸毒后的快感吧。 没有快感,世界上哪来的瘾君子呢?
解决这个问题的关键步骤是和模数转换器打交道。脑电和肌电都是连续性的生物电信号,其它生物信号如体温也可以通过各种传感器器转化为连续性的电信号。所谓的连续性是指这些信号在时间和振幅上都是连续的,可以无限细分。但是这种信号是无法被计算机所理解的,必须以一定的频率进⾏采样,将不同振幅的连续性的模拟信号转化为间断性的数字。模数转换器的功能就在于此。 在一定的时间点上,模拟信号有一定的幅度,可以⽤数字来接近。 取样频率越高,就越接近真实情况。对于脑电信号来说,每秒取样100-200次可以满⾜我们的需求。取样的精确度也依赖于模数转换器的电位分辨能力。例如,⼀个输⼊范围在正负5伏的12比特模数转换器,可以将这个范围内的电压⽤4096(2的12次方)个等级来区分。其本身的电压分辨率就是10000毫伏/4096=2.44毫伏。如果信号进入模数转换器之前已经被放大1000倍,那么这个模数转换器对原始信号的分辨率就是2.44微伏。如果把脑电放⼤大5000倍,那么对信号⼤小的分辨率就会达到微伏以下。
最初的努⼒是利用实验室的第一台自动记录设备,英国CED公司的数据采样系统。模数转换器大多有多个通道,这个采样系统有16个通道。模数转换器的工作方式是这样的:每⼀个循环中要依次把每个通道的模拟信号转化为数字,按同样顺序将其传输,并放在计算机的内存里。然后再进⾏下⼀轮的扫描、转换和传输,如此周而复始。其扫描速度,起始和终止,数据存放地点,数据的分流,实时的信号加工,数据及图形的显示和资料在磁盘的存放等各种操作都由计算机程序来控制。每过⼀段时间,同⼀处内存⼜会被新资料刷新。程序决定如何把流动的资料及时截获,分发给原来的通道,再进⾏加工。 我想做的第一步就是写出这样一个收集资料的程序,⽽第⼆步则是写出更高效直观和⽅便的程序,⽤来分析数据。
对我来说,这是⼀个未知的领域。我没有船只,但有造船⼯ 具(C语⾔)和造船的材料。我还有想象中的目的地,在⼤海中的某个地方,但是没有海图。无知者无畏。我决定兼任造船手和⽔手,冒险试一下。不管在哪个领域,科学探索总是指向未知世界。套⽤用一位大作家的话,失败之路各有各的不同,⽽成功之路总是要通过艰难险阻。写程序的好处是,除了损失时间和耗费脑⼒之外没有其它风险。虽然不知能否成 功,但是我相信,这是一次很有刺激性⽽风险并不太⼤的探险活动。
新造船手很快就设计出蓝图,软件的总体思路和架构设计没有⼤问题,但是在具体实施过程中遇到了不少麻烦。英国公司的说明书有关于模数转换器器的功能描述和指令集,但却没有多少使⽤例句。必须逐句摸索,写出⼩程序,反复测试。必须把船开动,然后操作仪器看看能开到哪里。 其间,这艘船有不少次无动于衷,消极怠工,循环打转,甚⾄触礁搁浅。 但是既然已经启航,绝⽆半途而废的道理, ⽆非是从头再来。另⼀个麻烦是如前所述的C语⾔指针问题。我常常把指针搞错,结果程序拒绝听令,动辄罢⼯或者擅⾃行事。因此,我反复给程序动手术,慢慢制服了这个执拗而癫狂的精神病人。
还有⼀个麻烦是计算机内存太⼩小,难以同时保持和显示资料及其图形。为此,免不了反复想象如何在存储空间里搬运数据。小时候就听说过韩信⾛⻢分油的故事。韩信骑⻢在路上见到两个人,为分油而发愁。他们有十斤油,想各分五斤,但却只⼗斤、七斤和三斤的容器。韩信⻢不停蹄,边⾛边说,只消如此这般,油即可分。此刻我也感到分油的难处,面对许多资料,却没有足够的容器。 找不到韩信,只好⾃己慢慢折腾。 经过数星期的努⼒,反复辗转腾挪,总算把油分好。程序运⾏成功。
然而,不完成我的第二步计划(开发快递分析记录资料的程序),前面的努⼒都是徒劳⽆功的。⾏百⾥者半九十,不能半途而费。研究人员在分析原始睡眠资料时,是把睡眠记录分成很多⼩片段,然后根据脑电图、肌电图和眼动电图的波形来判断每个片段的睡眠和觉醒状态。人类睡眠图的标准片段长度是30秒,分为觉醒、⾮快眼动睡眠和快眼动睡眠等⼏种状 态。非快眼动睡眠⼜分为四期(后来⼜改为三期)。⽽在⼤鼠和⼩鼠等实验动物,分析片段的长度则较 短,多以10秒钟为单位,基本上分为觉醒、非快眼动睡眠和快眼动睡眠三种状态。与其它程序不同,我所采用的方法是在计算机屏幕的上部显示三段10秒钟的记 录,在下部显示20或30分钟的记录。这样就使研究者能够同时观察脑电细节和动物较⻓时间的⾏为状态,这不仅十分有助于正确的判断, 而且可以⼤大加快了分析速度。因为动物时常连续处于一种状态,这在压缩的图形中⼀⽬了然。此时只须⽤几次点击就可飞快分析完20-30分钟的资料。为了使分析结果一目了然,我又将分析过的片段以不同的颜色显示:红⾊是觉醒,绿⾊是非快眼动睡眠,⽽黄⾊则是快眼动睡眠。 这种直观的显示提供了即时的反馈信息, 便于及时纠错。以上两个⽅面算是独家发明,我在其后的新版软件中也一直延续了这个风格。同事们非常喜爱这种独特的设计风格,它使睡眠分析工作显得不那么枯燥, 脑电图的波形有了亮丽的色彩。 有位同事对我说,“我现在对这个程序有点⼉上瘾了,可以一边喝咖啡一边分析资料了。” 他的话令我欢欣鼓舞。不过,我分不清他的快感是来⾃程序还是咖啡。 ⼼理学家研究动物的行为,发明了操作式条件反射。动物按⼏下杠杆,就可以获得一点奖赏。 于是动物就学会了尽量多按杠杆或快按杠杆,以便得到更多的奖赏。 后来这种方法成了研究药物成瘾的标准方法,通过压杠杆或者舔⽔瓶就可以获得可卡因或者吗啡类药物的注射奖赏。 现在我的同事敲⼏下键盘就喝一⼩口咖啡,自觉地做了药物成瘾研究的受试者!
软件设计出来了。虽然仍有毛病,但是分析效率相当于原来软件的三倍。后来,我⼜数次改进这个DOS环境下运⾏的软件,再进⼀步开发出新的Windows程序。而新的程序在手动分析睡眠方⾯的效率⼜进⼀步提高,达到CED软件公司软件分析效率的四倍。迄今为⽌止,这也许还是动物研究中最高效的睡眠分析软件,可以在半小时内分析24小时的原始资料。与此相⽐,日本公司开发的SleepSign软件可以做半自动的原始资料分析,但是验证24小时的睡眠资料却需要2个小时。
殊死搏⽃
快乐总是暂时的,而苦闷一定是长久的。随后实验室需要更多的自动记录系统,可是一台记录仪要五千多美元,实验经费⾥没有这笔钱,只能另谋出路。我经过反复查询,找到了南⾮一家公司生产的模数转换器PC30,其性能不亚于英国公司产品内的模数转换器,而价格却不到五百美元。于是我们买来这个产品,研究产品的驱动程序,从头改造⾃己的记录软件。另外⼜给模数转换器加上硬件的连接界面。总算上天眷顾,经过⼏个星期的艰辛劳动。这次⼜是如愿以偿。很快,实验室的研究者们一致同意将其运用到所有的记录系统。
编程的另一个辛苦是要应付软件和硬件的升级。不能跟上变化就意味着被淘汰,或者说是死亡。操作系统由DOS改为Windows是⼀大进步,但是这给写程序带来了新的挑战。 原有的编程模式和习惯都不适应新的环境了,旧的程序模块只有⼀小部分可用。随之而来的是编程工具的更新换代,程序员只能跟随潮流而动。操作系统由32位升级到64位系统,也面临着类似的问题。
公元2000年年底,我来到宾夕法尼亚州立大学医学院工作, 有了⾃己的实验室。有一天,系⾥的⼀位⽼教授给了我⼀个National Instruments公司的模数转换器。我⼀看,喜出望外。原来的模数转换器都是16通道的,⽽这个是64通道的,可以大大扩展记录能力。于是我重复原来的过程,⽤几个星期的时间把软件更新。与此同时,我⼜从DOS版本的软件过渡到Windows版本。这可以说是⼀个质的飞跃,增加了许多功能。以前颇受内存不足的问题困扰,在Windows的环境下,这基本上不是⼀个问题了。但是好景不长,计算机的发展推动了USB界面,新的计算机逐渐缩减了主板上的扩展槽。越来越多的模数转换器采⽤用USB界面,而我以前应用的模数转换版都依赖主板上的ISA或PCI扩展槽。应用USB界⾯意味着安装的简化,而且在手提计算机上也可以做自动记录。如果跟不上新的发展,老旧计算机逐渐退出历史舞台,原有的模数转换器及相应的软件就会变成⽆用之物。 ⽣存还是毁灭? 哈姆雷特的问题⼜一次来临。
正当我翘⾸以待之时,噩耗传来:National Instruments公司的驱动软件不再支持Borland公司的编程工具,只支持微软公司的编程工具。这是⼀个市场战胜技术的决策。 Borland是个小公司,其产品虽然更好,但是在微软这个巨无霸面前没有胜算。各个公司的软件部门都倾向于选择微软公司的产品,这样出了问题也不会受到指责。在激烈竞争的环境下,谁知道Borland这个小公司还能生存多久?别无选择,只能早做打算。 我经过广泛搜寻,终于找到另⼀家⼤公司Measurement Computing生产的64-通道模数转换器,它⽤的是USB接⼝,而且支持Borland公司的编程工具。更有吸引⼒的是,其模数转换器的价格⼜便宜一半多。而此时Borland公司已经改组,最后消失, 其编程工具及团队卖给了另一家公司。同样的过程,周⽽而复始。我对这种变化已经司空见惯,但对于新的技术仍然不失热情。此刻已经有几个实验室希望应⽤我开发的软件,这似乎也是一个小的机会。有了前几次的经验,修改程序只是时间问题。此后我终于过了⼗几年的平安生活,将编程的兴趣转到其它⽅面。但是天有不测风云,⼈又岂能预料?十⼏年前开始,64位的Windows操作系统逐渐成为主流。Measurement Computing公司也不失时机地宣布,其64位系统的驱动程序不会支持Borland系统的编程工具。希腊神话中的西西弗斯善于和死神作对,后来受到惩罚。他每天的工作就是把一块巨⽯由山脚推到山顶, 巨石随后滚下,他再重复同样的⼯作。⻄西弗斯的命运就是业余程序员的命运:生存必然要劳苦。在没有决定何时⽤安乐死结束程序员生涯之前,唯有⾟苦地活着。程序员又被称为码农,常年在软件园地⾥耕耘。 农⺠看一条条的垄沟,码农看⼀⾏行的代码。两者追求丰收的爱情之旅都是一⾸长恨歌。美满总是“上穷碧落下⻩泉,两处茫茫皆不见”。用李商隐的诗形容码农命运就是,“春蚕到死丝⽅尽,蜡炬成灰泪始干。” 与职业码农相比,业余码农的执迷不悟也是不遑多让的。
经历过编程工作的辛苦,也就不难理解为何软件的知识产权特别重要。每⼀款软件的开发都要投入⼤量和复杂的劳动,其中凝聚着开发者的智慧和⼼血。盗版软件者是直接掠夺别人的劳动成果和财产,是死神的帮凶。对于软件开发者来说, 盗版软件是巨大的威胁。每⼀个走向文明的社会都要走向保护知识产权,不然就无法发挥软件开发者的创造⼒。
追求自由
谈论程序员的自由似乎是可笑的。程序员不能随意选择所⽤的工具,必须遵从严格的语⾔规范,总是要不停地去捕捉失误。在很多时候,程序员不能自主。但是即便带着镣铐,程序员还是能够与狼共舞。编程的一⼤好处是不需要多少资源,⼀台计算机在⼿加上几个编程软件,就可以自由发挥。相比之下,生物医学⽅面的研究就要依赖更多的条件,需要更多的⼈力和物⼒资源(归根结底是金钱)。有了编程的能⼒,无疑使我在研究工作中获得了更大的自由。在研究经费匮乏之时尤其如此。做医学研究,研究者必须是⼀个指挥者。而像我这样的业余程序员却更像一个独⾏侠, 时常于夜深⼈静之时作案,一旦得手,便欣然自得。独立性和自由度,是业余程序员可以引以为傲之处:写程序是自主自发的行为,写与不写,写什么与何时写,全凭自⼰做主,视心情⽽定。
与职业程序员相⽐比,业余程序员的独⽴性和自由度似乎更加明显。在知识结构和编程能⼒方面,业余程序员有着很明显的短板。我⾄至今仍然对网络技术⼀无所知,对视频音频信息的处理束手⽆策,对许多编程技术也不熟悉, 对新的编程工具感觉茫然若失。但是对比专业程序员,业余程序员也并非全无优势。 作为软件的第⼀个用户,他可以更深刻地理解用户的需求,开发出切实有⽤的功能,优化最关键的环节,设计最顺手的界面。职业的程序员大多是团队工作,各自有明确的分工,遵守一定的规程,保持既定的界面,这对于开发大型软件是必不可少的。⽽业余程序员则更少束缚,有更多的发挥空间。开发小型独立的应用软件是独行侠能够一显身手的地方。业余程序员虽然技不如人,但是却在某些⽅面更容易发挥出创造力。而这也确实对我的工作有很大的帮助。
由于在研究中要与⼤量的数据打交道,我⾃己编写了一些用来记录和分析睡眠资料料和动物行为的软件。有了编程的能⼒,就可以解决一些看起来无法解决的问题。 ⼀个好的程序可以使工作效率提⾼许多倍。例如,在睡眠研究中,常常需要测量睡眠的深度。这依赖于对脑电波做频率和振幅的分析,也就是快速傅⾥里叶变 换。 没有计算机程序,这是极不现实的运算。而借助于软件,计算的效率可以提高亿万倍。多年来,我的同事、合作者和同⾏们应用我编写的软件做了许多研究。 以此为基 础,他们发表了数百篇论文。 这其中大部分的研究是在其它实验室做的,我只是在其中数篇文章中挂名。而我曾经参与的70几项研究中也应用了我所开发的软件。按照哲学上的一种解释,自由是对必然的认识和掌握。当一个人能够对事物有所认识,能够通过自身的努力改变事物甚至自身命运的走向时,他就在一定程度上获得了自由。
内心狂野
前面所说的经济和研究⽅面的自由度主要还是从功利的角度看问题。 在我看来,编程最令人兴奋之处还是这种工作本身的性质。思想⼒得以自由释放,想象⼒能够自由飞翔。这才是编程工作的有趣之处。编程固然会带来许多苦恼,但其中也藏着许多快乐。在编程过程中,⼀个爱思想的人不难体验到思维的快乐,也不难感受到其中的艺术成分。程序员以沉静的外表隐藏活跃的神思,凭枯燥的代码发挥潜在的魔力。孤灯下凝神静气,银屏前谋篇布局。书写代码,如排兵布阵,必须井然有序。代码能体现出程序员个人的意志,思路的清晰,逻辑的严整,和精湛的技巧。⼀个好的程序结构分明,层次清晰,功能明确。这样的程序写起来从容舒畅,读起来赏心悦目。代码虽然枯燥,但也有简洁之美。解决问题⽆一定之规,时而可有神来之笔。在功能的构思、界面的设计、难题的解决和错误的排除等⽅面,不仅需要缜密的思维,也需要想象⼒和创造力。⼀个文件包含上亿数字, 并⾮罕见。数据的复杂程度,可以从一维、二维、三维上升到七维和八维。面对这些数据,就需要把它们在大脑的空间里展开,折叠,旋转,变换,处理。从各个角度去思考和观察数据,以不同手段去解决问题。 有时又须用透视之眼观察数据,凭无形之⼿去抓取数据,以现身之术令其暴露原形。身为程序员⽽无思维之快乐,实乃不幸。
在程序员手中,数据是精灵,是生命,是甲兵。
数据是精灵:毕达哥拉斯学派把数当作世界的本质,这其中自有几分道理。世间万物,无不遵从数理的支配。 数学家可以挑战上帝的权威。上帝无法改变一加一等于二的道理,⽽数学家却发明了布尔代数:使一加⼀等于一。 上帝不喜欢掷骰子,数学家却发明了概率论。数存在于万物之中,⼜如精灵般可以脱离其具体形态,随意游⾛于抽象和具象之间。数是信息最抽象的载体,没有数就谈不到信息和智慧。从古到今,⼈类对数的认识不不断深化:正数、负数、整数、有理数、⽆理数、实数、虚数和复数。借助于数学家的发明,一个业余程序员也可变身为数字的魔术师,使数据随着他的意志而流动于不同的载体(磁盘和内存),在CPU内脱胎换骨,转瞬⼜可随势赋形,现身于银屏,跳跃于眼前。
数据是生命:因脑电波⽽荡漾,随肌⾁而收缩,同呼吸而起伏,伴心脏⽽跳动。通过数据我们可以看到丰富多彩的生命过程:睡眠和觉醒,运动和静止,学习和记忆,健康和病态。从数据中可以体验到生命的美妙和科学的魅力。生命是信息的海洋,程序是科学的探险器。于噪声中辨信号,纷扰中察趋势,变幻中寻规律,假相中求真知。程序员挥灵性出新意,运幽思成佳构,凭技艺勾细节,以生命铸神器。
数据是甲兵:屯于深山,隐于幽谷;伺机而动,应时而发。程序员看数据:形如矩阵,⾏列分明;状若长蛇,首尾相衔;进退有序,循环无穷。常言道,兵无常势,⽔无常形。此话用于数据演算极为恰当。程序员推演数据,如韩信点兵,多多益善。试看大将发令:旗号分明,阵型变幻;阴阳颠倒, 时空旋转。对比程序演算,也颇为有趣。两军对垒,时常倚多为胜;数字相交,偏爱以少胜多。恰如一将得令,千⾥单骑,过五关斩六将,乃数据运算中寻常之事。 数据厮杀:或左右不分,或奇偶有别。譬如火攻:周郎羽扇纶巾,谈笑间樯橹灰⻜烟灭;陆逊火烧连营,却是隔一营烧一营。兵家有云,多算胜,少算不胜。韩信能够⾛马分油,能够以十⾯埋伏击败项羽,大概与其对数字演算的偏爱有关。 程序员痴迷于推演,尤胜兵家。莫笑手⽆寸铁,偏爱纸上谈兵。业余码农也是威⻛凛凛的大将军。
编程是⼀种创造性的智⼒活动, 需要的不仅仅是知识和技巧,还要有想象⼒,推理能⼒、探索和解决问题的能力。程序员的工作其实是在做各种思维实验。遇到一个问题,要设想解决方案。有时要面对多种选择,要比较利弊,从中找出最佳方案。然后进⾏编写,解决各种技术问题。程序员在头脑中要想象各种指令的逻辑关系,程序运⾏的步骤, 数据的时空转换和预期的效果。然后通过程序的运⾏对自⼰思维进⾏验证。这个过程是⽢苦参半:出错带来无穷苦恼,成功带来巨大奖赏。思想的流动,绝⾮一贯通畅无阻。遇到障碍,或从头越过,或迂回前⾏。有时千思万想,满腹愁绪;困于一隅,苦⽆出路。 积以时日,暗流涌动,忽得灵感,豁然开朗。 于是堰塞之湖,决堤而出,顺势⽽下,⽆比酣畅。 此时此刻,⾼山流水,恨无知音。每⼀次战胜困难后的成功,都是对⼈的自主性的肯定, 是⼈生的自我肯定。我相信,每当此刻,大脑里的内源性鸦⽚系统和多巴胺奖赏系统一定是激活了。
⼏年前不慎误入诗词小道,感觉虽然⽆用,但写诗的自由度⽐编程⼜⾼了许多。编程必须老老实实,写诗却是可以吹牛的。下⾯这⾸诗就是通过吹牛把编程上升到了飘飘然的境界。
编程 蓬草 写于2017年9月14⽇ 洞晓微机神⽓凝,灵光初动本无形。 幽思变幻藏丘壑,代码腾挪胜甲兵。 处处心裁唯缜密,源源指令各分明。 功成一键传天外,谁解今宵此刻情。
不知何时,我似乎有了一个新的梦想。除了作为⼀名普通的睡眠研究者之外,我又有了新的身份定位:程序员中的诗人,诗人中的程序员。将生命的潜⼒发挥到极致,乃此生之奢望。
人⼯智能已经在许多领域超越人类,目前正在向艺术领域发展:可以绘画、谱曲和作诗。独创性是科学和艺术的本质之一,我相信⼈工神经网络也会展现出越来越多的独创性。⼈类和⼈工神经网络在艺术⽅面总有一拼。在有⽣之年,写诗这个新爱好会不会在与AI对决中落败?隐约中既有几分⾃信,也有⼏分疑惑。但是落败⼜有何妨?艺术是独特的。胡适说得好:你不能做我的诗,正如我不能做你的梦。AI可以写我的诗,做我的梦吗?
|