- 编程风格:程序设计与系统构建的艺术(原书第2版)
- (美)克里斯蒂娜·维代拉·洛佩斯
- 1648字
- 2025-04-15 17:59:02
1.3 评注
在这种风格下,程序反映了运行它的、受限制的计算环境。内存限制迫使程序员必须在有限的可用内存中轮流装载数据,这增加了计算任务的复杂性。此外,标识符[1]的缺失导致程序文本中缺少描述问题的自然术语,需要通过注释和文档来增加程序文本的可读性。这就是20世纪50年代初期关于编程的全部内容。然而,这种编程风格并没有消失,它如今仍旧被使用在直接处理硬件或者需要优化使用内存的场景中。
对于不习惯这类约束的程序员来说,示例程序看起来可能很陌生。虽然这是一个与Python或任何现代编程语言都无关的程序,但它很好地体现了本书的主题:编程风格源于约束。很多时候,约束是外部强加的——也许是硬件内存有限,也许是汇编语言不支持标识符,也许是性能很关键(必须直接与机器打交道等)。其他时候,约束是自己强加的:程序员或整个开发团队决定坚持某些思考问题和编写代码的方式,原因很多——考虑可维护性、可读性、可扩展性、问题域、开发人员过去的经验等。或者简单地说,就像这本书的情况一样,在不涉及新的语法的情况下教授低级编程语言相关的编程内容。事实上,几乎可以用任何编程语言编写低级的往日美好风格的程序!
在解释了这种不寻常的词频任务实现的原因后,我们深入研究一下这个程序。内存限制使得我们不能忽视要处理的数据的大小。在本示例中,我们自己设置了1024个存储单元(第13行)。“存储单元”在此处有些模糊,被用来粗略地表示一段简单的数据,例如字符或数字。鉴于像《傲慢与偏见》这样的书包含的单词数远远超过1024个,我们需要想办法一小块一小块地读取和处理数据,并且大量使用“辅助存储器”(文件)来存储暂时无法被装入主存储器中的数据。在开始编码之前,我们需要确定主存储器(内存)中保存什么、将什么转存到辅助存储器,以及何时保存数据(请参阅第16行到第26行中的注释)。那时和现在一样,访问主存储器(内存)比访问辅助存储器快几个数量级,因此这些计算是为了优化性能。
还有许多选项可以采用,我们鼓励读者探索这种风格的各种解决方案。示例程序分为两个不同的部分:第一部分(第28行到第95行)处理输入文件,计算单词出现的频率并将该数据写入词频文件;第二部分(第98行到第125行)处理中间文件(词频文件),以便发现前25个最常出现的单词,并在最后打印出它们。
程序的第一部分的工作原理如下:
❑在主存储器中存放停用词,大约500个字符(程序文本第33行到第36行);
❑每次读取输入文件的一行,每行最多只有80个字符(程序文本第50行到第95行);
❑对于每一行,过滤字符、识别单词并将它们规范为小写(程序文本第59行到第95行);
❑从辅助存储器中读取或者向辅助存储器中写入单词以及它们的词频(程序文本第73行到第90行)。
像这样处理完整个输入文件后,接着我们将注意力转向中间文件中已积累的单词和词频。我们需要的是已排序的最常出现的单词的列表,因此程序执行以下操作:
❑在内存中维护一个有序列表,存放当前25个最常出现的单词及其频率(程序文本第104行);
❑从文件中每次读取一行,每行包含一个单词及它对应的频率(程序文本第108行到第113行);
❑如果新单词的频率高于内存中的任何单词,则将其插入列表的适当位置,并删除列表末尾的单词(程序文本第116行到第120行);
❑最后,打印前25个单词及其频率(程序文本第121行到第123行),并关闭中间文件(程序文本第125行)。
如你所见,内存约束对所采用的算法有很大影响,因为我们必须时刻关注内存中存储了多少数据。
第二个自己强加的约束是:没有标识符。第二个约束对程序也有很大的影响,但这种影响的性质不同:它影响的是程序的可读性。程序没有变量,只有一个数据存储器,该数据存储器可以通过数字索引的方式来访问对应的数据。问题的自然概念(单词、频率、计数、排序等)在程序文本中完全不存在,而是间接被表示为内存的索引。重新引入这些概念的唯一方法是:添加注释来解释内存单元保存的数据类型(例如第37行到第43行,以及第102行到第105行中的注释)。在通读程序的时候,我们经常需要回到那些注释处,才能了解某个内存索引对应的高级概念是什么。