购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

Chapter 4
第4章
单体风格

4.1 约束条件

❑没有命名的抽象。

❑不使用或很少使用库。

4.2 此编程风格的程序

4.3 评注

在此编程风格中,即使我们能使用具有强大的库函数的现代高级编程语言,但问题几乎仍旧以“往日的美好”风格解决:一段代码从头到尾不使用新的抽象,也不使用现有库函数中已有的功能。从设计的角度来看,主要的关注点都在如何获得期望的输出结果,没有考虑如何分解问题,以及如何利用已有的代码。鉴于整个问题是一个单一的概念单元,编程任务包括:定义数据和控制该程序的控制流。

示例程序的工作原理如下。它包含一个全局列表变量word_freqs(第5行),用于保存单词和与之相对应的词频。该程序首先从文件中读取停用词存放到列表,然后对它进行扩展以包含单个字母的单词,例如a(第7~9行)。然后程序进入一个长循环(第12~46行),逐行遍历输入文件。在该循环内,还有第二个嵌套循环(第15~45行),它遍历每一行的每个字符。在第二个嵌套循环中,要解决的问题是检测单词的开头(第17~19行)和结尾(第21~43行),增加每个检测到的非停用词的单词的频率计数(第26~43行)。对于之前没有出现过的单词,将新的单词、词频对添加到词频列表word_freqs中,其频率计数为1(第35~36行);对于已经出现过的单词(第29~34行),只需递增计数(第31行)。由于我们想按词频降序打印出单词和对应词频,因此该程序进一步确保词频列表始终按频率降序排列(第39~43行)。最后(第48、49行),程序只需简单地输出词频列表的前25个条目即可。请注意,从Python语言的标准库中导入的只有sys和string(第2行)。

在计算机编程的早期,人们使用的是低级编程语言,编写的程序也相对较小,并且也只有这一种风格。诸如goto之类的结构提供了有关控制流的进一步表达能力,但也因此产生了冗长的“意大利面条式的代码”。这种结构被认为对除最简单程序以外的所有程序的开发都是有害的,并且在大多数情况下现代编程语言中都没有这种结构。但是goto语句并不是产生单体风格程序的根本原因,如果恰当地使用goto语句,同样可以编写出漂亮的程序。从维护的角度来看,潜在的坏处是存在长程序段,并且这些程序段无法为读者提供关于该程序段的功能的更高级的抽象。

使用任何编程语言都可以编写单体风格的程序,如示例程序所示。事实上,在现代程序中看到长程序段的情况并不罕见。在某些情况下,出于性能原因或因为无法轻易分解(拆解)正在编码的任务,这些长程序段是有必要的。然而,在多数情况下,这可能是程序员没有花时间仔细思考计算任务的结果。单体程序通常很难理解,就如同没有分章节的手册一样难以理解。

圈复杂度是一种度量标准,用于衡量程序的复杂性,特别是控制流路径的数量。根据经验,一段程序的圈复杂度越高,难以理解的可能性就越大。计算圈复杂度CC时将程序视为有向图,它由以下公式给出:

CC= E-N +2 P

其中, E 指边的数量, N 指顶点的数量, P 指出口节点的数量。

例如,考虑以下程序文本:

本段代码的有向图如下(节点号对应行号):

该段程序代码的圈复杂度为:

CC= E-N +2 P =5-5+2=2

圈复杂度与测量自然语言文本可读性的指标具有相同的意图,例如Flesch阅读容易程度测试和Flesch-Kincaid阅读级别水平测试。这些指标试图将风格概括为一个数字,并获得一些心理学证据来表示写作风格对人们理解文本的难度。显然,写程序和写文学作品是不一样的。但是,当谈到理解全部写作内容时,它们有很多相似之处。在某些情况下,可能需要长程序代码来向读者阐明编程任务的内在复杂性。但是,更多时候可能没有必要。

4.4 系统设计中的此编程风格

在系统层面,单体系统体现为拥有单一的大型组件,可以完成应用程序需要做的所有事情。这与将系统分解为不同的子模块组件,而每个子模块组件负责特定的功能的风格形成了鲜明对比。

这种风格在所有层面上都被认为是不好的做法。但是,单体程序却很常见。识别单体程序并尝试理解导致它们的原因很重要。

4.5 延伸阅读

Dijkstra, E. (1968). Go To statement considered harmful. Communications of the ACM 11(3):147-148.

概要:Dijkstra强烈反对GOTO。一篇经典论文。

Knuth, D.(1974). Structured programming with go to statements. ACM Com puting Surveys 6(4): 265-301.

概要:Dijkstra对GOTO语句最好的反驳。

McCabe, T. (1976). A complexity measure. IEEE Transactions on Software Engineering SE-2(4): 308-320.

概要:基于图形的FORTRAN程序的复杂性度量。首次尝试量化各种程序设计决策的认知负荷。

4.6 词汇表

控制流 :执行程序语句和计算程序表达式的顺序。包括条件、迭代、函数调用、返回等语句的顺序。

圈复杂度 :一种软件指标,测量程序源代码中线性、独立执行路径的数量。

4.7 练习

1. 用另一种语言实现示例程序,但风格不变。

2. 示例程序每次从文件中读取一行。修改它,使其一次将整个文件加载到内存中(通过readlines()函数),然后遍历内存中的每一行。如果使用这种方式,是更好还是更坏?为什么?

3. 在第37~42行,示例程序可能在每次检测到单词时重新排序词频列表,以便它始终按词频值递减排序。在保持单体风格的前提下,修改程序,将重新排序的工作安排在位于程序最后的单独循环中,并在此之后,再在屏幕上打印单词频率。这样做的利弊是什么?

4. 示例程序的圈复杂度是多少?

5. 使用这种风格编写“导言”中提出的任务之一。 ovy7xNK2biYf2asPA0eWIRO2WitUP77i1VmdxGssqugTKT6EHxjJWrvote+p2CLr

点击中间区域
呼出菜单
上一章
目录
下一章
×