程序员的思考方式比较有意思,并且这些思考方式有时候表现得很好。这些思考方式其实可以概述下,通常包含如下几个点:

一切都只是数据。 数据本身没有任何意义,如果有意义那么它必须被解释。 编程是关于创建和组合抽象。 模型是给计算机用的,视图是给人用的。 偏执使我们富有成效。 更好的算法胜过更好的硬件。 工具塑造了手。

然而,一些人指出,这个列表是无用的,因为缺少一个更好的词:它没有说明数据和算法是如何被滥用的。令我感到尴尬的是我自己没有发现这一点,因为如果说过去几年教会了我们什么的话,那就是没有所谓的“纯技术”决定。关于收集什么数据、与谁共享、如何分析以及如何使用这些分析结果的每一个决定都必然且不可避免地会促进某人的兴趣。因此,我认为我需要在第 2 条和第 3 条之间插入另一个原则:“没有任何解释或使用是中立的。”

在我这样做之前,让我们按顺序处理这些。首先,也是最重要的,它只是数据。购物清单、电子邮件、遥远星系的图片:在计算机内部,它们都只是 1 和 0。甚至程序也只是数据:事实上,这是构建所有现代计算的关键洞察力。程序的源代码只是一堆文本文件,和论文没什么区别。一旦该文本被编译或加载到内存中,它也只是字节,并且推动这些字节与更正地址列表中的拼写错误或更改图像文件中像素的颜色没有什么不同。如果你理解这一点——如果你理解程序只是另一种数据——那么你所做的每一点编程都会变得更容易。

第二个想法是对第一个的补充。 数据本身没有任何意义——它必须被解释。三十二位01100100011000010111100001100001同时是数据字,整数1,684,108,385,浮点数1.6635613602263159e+22,略带透明的蓝灰色像素,左边音量稍大的中等音量样本通道,一条指令,用于将寄存器 100(共 128 个)的内容复制到内存中的某个位置,该位置比当前存储在寄存器 116 中的地址高 97 个字节,或者是 CAT 扫描仪中超导磁体之一外边缘附近的一个点如果这四个字节所在的文件是 CAD 模型。

原因是机器不理解;他们服从。如果你看这张图片,你会情不自禁地看到“数据”这个词:

[图片]

机器没有;它甚至看不到灰色背景上的四个蓝色像素点,因为它“看不到”任何东西。计算机将此图像和程序存储为内存中的位。如果这些位恰好对应于计算机 CPU 的指令,并且如果这些指令恰好组成了一个程序来检测图像中的形状并将这些形状与字母表中的字母匹配,那么计算机很可能会输出“数据”,但是它不明白。将变量称为“温度”并不意味着计算机将在其中存储温度——如果将变量称为“压力”或“弗兰肯斯坦”或“a7”,它会做完全相同的事情。

这给我们带来了第三个想法:编程就是创建和组合抽象。我们的大脑一次只能跟踪几件事情,所以如果我们想了解某事,我们必须把细节塞进盒子里,然后贴上标签,比如“找到最大值”或“病人记录”。

使抽象工作的关键之一是分离接口和 实现。接口是某物知道如何做的事情:它可以回答的问题,或者它可以执行的操作。它的实现就是它如何做这些事情:它存储什么数据,它使用什么算法,等等。可以有几十种方法来实现特定的接口;如果我们做好我们的工作,我们就不必关心实现,直到出现问题或我们需要提高它的性能。“请告诉我细节”在现实生活中可能很粗鲁,但在编程中却是必不可少的。

使抽象起作用的另一个关键是始终选择清晰而不是聪明。正如 Brian Kernighan 曾经说过的那样,“……首先,调试的难度是编写程序的两倍。所以如果你在编写它时尽可能聪明,你将如何调试它?” 程序是人类创造的最复杂的事物之一。在这里和那里使用小技巧来使它们更小或更快可能很诱人,但有人(也许是你)将不得不在以后再次弄清楚,实际上,没有人喜欢被欺骗。

抽象原则的一个含义非常重要,足以成为我们的第四个原则:模型用于计算机,视图用于人。模型是对计算机易于操作的事物的表示;视图是一种显示人类可以理解的部分或全部模型的方式。例如,一个 HTML 文档由具有属性的元素组成,这些元素包含其他元素或原始文本块:

图片

该模型可以在浏览器中呈现,为视力受损的人转换为语音,或者使用尖括号、引号和一些缩进显示为文本。这些都不是模型:它们都是使模型的内容可供不同上下文中的人访问的视图。模型本身不仅让计算机更容易使用:它是必不可少的,因为正如我们之前所说,计算机无法“看到”我们为人类创建的视图。

将其中一个视图转换回模型非常困难:解析 HTML 的文本表示需要数千行代码,而执行 OCR 或语音识别以翻译呈现的页面或其口语等价物可能需要数百万行。因此,第四大理念意味着结构化数据优于非结构化数据. HTML 页面的文本表示中的标签和属性之所以存在,是因为没有它们,计算机无法判断某些内容是斜体是因为它被强调了还是因为它是一本书的标题。借用乔恩·乌德尔 (Jon Udell) 的一个例子,一个带有卡通标题的 PDF,其标题为“编织圈在每个月的第二个星期二相遇”,这比一团 iCal 格式的文本更容易让人理解,但是第二个对计算机来说要容易得多。

我们之前说过,当出现问题或需要提高性能时,我们只想关心某些东西是如何实现的。第五个大想法是关于出错的事情,可以用偏执使我们富有成效来概括。提高生产力的最好方法——实际上也是唯一的方法——就是提高质量,而这在我们编写第一行代码之前就开始了。“我想数一数这张照片中的所有星星”说起来容易,但它的实际含义是什么?什么构成了明星?你什么时候决定一个块状像素点是两颗星而不是一颗,或者三颗而不是两颗?每个程序都包含有关此类问题的决定,即使您没有意识到存在问题并且您做出了选择。我们越早担心这个,我们浪费在构建错误事物上的时间就越少。

当然,一旦我们输入了代码,我们就不会停止担心。我们检查数据的格式是否正确,以防止“输入垃圾,输出垃圾”。我们在代码中进行检查以确保参数合理、数据结构一致、文件不为空等等。我们编写测试并使用构建系统来尽快捕获错误。起初,这可能会让人感觉慢下来,但研究表明它确实有效。

顺便说一句,应用此原则的最佳方法之一是使一切自动化。正如 Alfred North Whitehead 所说,“文明的进步是通过扩展我们无需考虑即可执行的重要操作的数量来实现的。” 我们编写程序不仅仅是因为我们想快速做事:我们编写程序是因为我们不想再做一些事情。版本控制系统为我们跟踪我们的工作;只要单个值发生变化,电子表格就会更新图表和汇总统计信息,等等。每次我们将一项任务自动化,我们都会减少下一次出错的机会,并有更多时间思考机器无法为我们做的事情。这不仅仅是一次节省:如果我们很好地自动化,那么额外的时间就会一遍又一遍地属于我们。

第六大想法是,我们可以考虑体现在程序中的算法有多快,更好的算法胜过更好的硬件。二十世纪最伟大的数学进步之一是算法复杂性的概念, 它的实际意义决定了我们用计算机所做的一切,无论我们是否意识到。基本思想是,我们可以估计一个算法将执行多少操作,或者它需要多少内存,作为我们试图解决的问题规模的函数。事实证明,一些算法会随着输入变大而缓慢减慢,而另一些算法会减慢很多,即使整个宇宙是一台大型计算机,它也无法解决任何大到令人感兴趣的问题。更快的芯片有帮助——很多——但速度的真正关键是专注于我们正在做的事情,而不是我们正在做的事情。

但是如果没有数据结构可以操作,算法就什么都不是,就像没有算法来操作数据结构毫无意义一样。这就是为什么这两个主题通常一起教授:带循环的数组、带递归的树等等。了解这种语言的语法或该库的 API 很有用,但优秀的程序员了解他们的数据结构和算法,就像水管工了解管道或音乐家了解音阶一样。

我们最后一个伟大的想法是工匠们几千年来都知道的东西:工具塑造手。构建软件会改变您使用软件的方式;让计算机做新事情会改变你对计算机能做什么的理解。这就是为什么任何有关计算思维的课程都应该要求您编写软件的原因:无论有时它多么令人沮丧,这是教您软件可以做什么的唯一方法。