5 循环坚不可摧,除非程序员出错
请回顾我们在这一章的开头用的一个可以从0数到10亿的循环:
top = 1000000000
i = 0
while i < top: i = i + 1
我记录了这段程序在我的计算机上运行的时间:不到一分钟。请记住,当这本书被印出来运到你手上的时候,计算机已经变得更快了。计算机畅通无阻地运行,就像你开着一辆高速汽车,在一条没有尽头的路上踩油门。但是,随着车内音响的轰鸣和体内肾上腺素的飙升,你很容易在超速行驶的时候忽略几英里(一英里约等于1.61千米)外的大石头,这可怎么办?没错,轰隆!你的车估计会报废,但愿你系了安全带。
当你将前文的计数循环与我的汽车比喻进行比较时,我希望你能思考两者之间的差别。汽车加速总会在某一刻达到最高速度。当它撞到石头后,至少在几秒钟内你将疼得龇牙咧嘴。你会有时间恢复知觉,走出火堆和废墟,最好只有几处轻微的皮肉伤。
可是,计算过程一旦启动,就会从被激活的那一刻起以最高速度运行。如果遇到某个错误,它会立即停止,连同它所处的整个世界一并消失。计算机被迫停止的这一瞬间,是一场彻头彻尾的灾难。因为当计算过程在正常进行的时候,你看不见它在做什么。但当它停止时,它要么明确地向你抱怨,要么干脆静止不动。我相信你也遇到过这种情况。你的屏幕上闪现一些信息,或者忽然一片空白。这样的事情在即将发生之时,通常毫无预兆——这通常会让你有点不高兴,甚至很生气。只要在网上搜索“计算机怒火”(Computer Rage),你就能感同身受。
在我们讨论计算机为什么会死机之前,先让我们来想想这对计算机来说是什么感觉。我能想到的最贴切的比喻是你在网上看到的许多“大神级”多米诺骨牌玩家煞费苦心地铺开数以千计的多米诺骨牌,然后打开摄像机看多米诺骨牌按完美的顺序倒下,直到其中一块被放错位置的骨牌……失败了。尴尬和震惊是真实存在的,唯一的办法是回到原点,从头开始解决所有的问题。
只需一块错位的多米诺骨牌就能摧毁数百块骨牌按完美顺序倒下的计划——就像正在运行的程序完全崩溃。当地板上到处都是多米诺骨牌时,专业的多米诺骨牌玩家有自律的耐心来修复和重做这一切,程序员也必须以完全相同的态度行事,他们需要以同样干脆的态度轻快地说:“它需要被修复。”如果计算机程序员每次在软件崩溃时都无法控制地发怒,他们将无法完成任何工作。由于软件经常崩溃,你会发现专业的软件开发者对灾难的容忍度非常高,但几乎无法容忍可以轻易避免的小错误。
想象这样一种工作,每隔几分钟你都可能被计算机告知做错了什么。运行的程序或计算系统越复杂,可能出错的地方就越多。这些错误分为三类:可避免的(“愚蠢的”)错误、较难避免的错误和不可避免的错误。在计算机发展的早期,许多软件系统和它们赖以运行的硬件存在缺陷,因为就像试验飞机,很多错误都属于“不可避免”的范畴。但如今这种情况没有那么常见,因为计算机已经变得非常成熟。例如,我在20世纪80年代编写计算机程序时,经常会遇到因一些早期机器的“实验室测试”性质而无法控制的错误。这些错误,或者说bug,最初是格蕾丝·霍珀在20世纪40年代在一台早期计算机的继电器里发现的一只被困住的飞蛾,这导致电流无法通过两个接触点,因此计算机无法正常工作。
当年,霍珀沿着复杂的机器布线发现了一只真正的bug
,这件事具有象征意义:不难想象她在把讨厌的飞蛾除掉后的喜悦,以及为解决这个问题展开的痛苦搜索。在计算机程序成堆的数字和符号中寻找,就像大海捞针。由于在软件中发现bug非常困难,因此在一个项目中协同工作的程序员总有一种强烈的偏见,认为在团队中出现容易避免的“愚蠢的”错误是可耻的。幸运的是,现在有各种系统和技术可以减少软件bug,但编写没有bug的软件对人类来说是不可能的。谨记,并非所有的bug都是致命的——许多类型的bug可能存在于软件中,却对软件的运行没有明显的影响。
为何我们无法构建一个没有bug的计算机程序?一种简单但令人不安的思考方式是想象你在开飞机。飞机是由数百万个部件组成的,想想有多少颗螺丝可能松动或丢失。同样地,像微软Excel这样复杂的软件有几千万行代码——写入或编译错误其中一行的可能性有多大?请记住,一个大型软件程序可能由成百上千人用数年时间开发。一颗松动的螺丝不太可能对飞机的飞行造成致命的影响,正如Excel代码中一个错误的数字设置不一定会导致它停止运行。但可以想象,如果散布各处的“良性”bug足够多,它们就会开始对彼此产生负面影响,意想不到的事情就可能会发生。
计算机可以孜孜不倦地循环。从一个计算机程序启动并开始按指令循环的那一刻起,它便进入了一个隐秘的广阔宇宙。在一个仅由数字组成的世界里,数字可以被自由、精确、毫无阻力地读写。根据软件的复杂程度,一个bug总有或大或小的可能潜藏其中,静候时机影响程序的运行,甚至可能使其停止。当程序停止时,如果软件开发人员可以找到bug并进行必要的修复,那么循环就能继续。更难发现的,是那些不会立刻导致问题,但有可能悄然无声地以难以诊断的方式带来麻烦的bug。
别忘了,你在计算机屏幕上看到的,只是计算机的无形世界中正在发生的一小部分事情。在循环和递归循环的驱动下,无数股数字信息流被或优雅或不优雅地快速处理。这一切都归功于人类计算者、人类硬件制造商和人类软件工程师。我们还要时刻准备在最不合适的时候,去应对由人类同伴偶然留下的无形bug的冲击。无论如何,请做好准备,迎接这样一个未来:计算机可以深入其内部,孜孜不倦地清除我们写入的bug——消除所有障碍,让它们强大的循环运动永不停歇。计算机将永远准确无误地重复自己。
[1] John Maeda, “First Use of the Word ‘Computer,’” How to Speak Machine (blog), February 24, 2019, howtospeakmachine.com/2019/02/24/first-use-of-the-word-computer.
[2] Michael Corballis, The Recursive Mind: The Origins of Human Language, Thought, and Civilization (Princeton, NJ: Princeton University Press, 2011), 1.