本书灵感

在20世纪40年代,法国作家Raymond Queneau写了一本名为Exercises in Style的书,书中以不同的风格书写了同一故事99次。这本书主要描述的是写作技巧,书中演示了许多不同的讲故事的方式。故事本身相当琐碎,情节始终相同,书中更突出形式,而不是内容。这本书演示了我们讲故事的方式将如何影响读者对故事的看法。

Queneau的故事非常简单,用两句话就可以讲清楚:叙述者在“S”巴士上注意到一个戴着帽子的长脖子男人与坐在他旁边的男人发生了口角,两个小时后,叙述者在Saint Lazare火车站附近看到长脖子男人和朋友在一起,这位朋友正在给这个男人提出一些关于他大衣上的一个额外扣子的建议。就是这样!作者使用反叙法、隐喻、万物有灵论等多种方式,对这个故事进行了99次不同的演绎。

作为多门编程课程的讲师,多年来我注意到:学生经常很难理解编写程序和设计系统的不同方式。他们接受过一种(最多两种)编程语言的训练,因此他们只了解这些语言所鼓励的编程风格,却很难理解其他风格。这不是他们的错。纵观编程语言的历史,加上大多数计算机科学课程中缺乏关于编程风格的教学材料的事实,人们只有在积累了大量经验之后,才能接触到这个问题。即便如此,编程风格仍被视为程序的一种无形资产,难以向他人解释——并且随之而来的是许多技术争论。因此,为了赋予编程风格应有之义,同时由于受到Queneau的启发,我决定开始用我多年来遇到的多种风格编写不同的代码来处理一个完全相同的计算项目。

那么,什么是风格呢?在Queneau的领域,Oulipo(乌力波,法语Ouvroir de la littérature potentielle的缩写,英译为“Workshop of Potential Literature”)团体认为,风格不过是“约束下的创作”的结果,通常基于数学概念,例如排列和组合。这些约束被用作一种手段来创造除了故事本身以外的一些智力上有趣的东西。多年来,这些概念流行了起来,许多文学作品基于Oulipo的约束被创作了出来。

在本书中,编程风格也是在一系列约束下编写程序的结果。约束可以来自外部,也可以是自己规定的;约束可以是来自环境的真正挑战,也可以是人为的;约束可以来自过去的经验和可衡量的数据,也可以来自个人偏好。与来源无关,约束是风格的种子。通过遵守不同的约束,我们可以编写各种不同的程序,它们的功能几乎相同,但实现的方式完全不同。

我认为,在优秀程序员必须知道的所有事物中,编程风格与数据结构和算法同样重要,但重点是人的影响而不是计算的影响。程序不仅向计算机传达信息,更重要的是向阅读它的人传达信息。与其他表达方式一样,所讲内容的结果受表达方式的影响。高级程序员不仅需要正确编写性能良好的程序,也需要能够针对不同的目的选择合适的风格来表达这些程序。

传统上,教授算法和数据结构比教授编程表达的细微差别要容易得多。关于数据结构和算法的书籍或多或少都遵循相同的形式:伪代码、注释和算法复杂度分析。而关于编程的文献往往分为两大类:解释编程语言的书籍和展示设计或架构模式集合的书籍。然而,从编程语言鼓励/强制执行的概念到最终构成程序的程序元素组合,在如何编写程序的(概念)范围内,有一个连续统一体;语言和模式相互依赖,将它们作为两种不同的事物分开,会产生错误。接触过Queneau的作品后,在我看来,他的关注点——将约束作为解释表达风格的基础,曾是统一编程世界中许多重要的创造性工作的完美模型。

需要指出的是,我并不是第一个将约束视为解释软件系统风格的良好统一原则的人。长期以来,关于架构风格的作品一直采用这种方法。我承认,风格源于约束(有些是不允许的,有些必须存在,有些则是有限制的,等等)的概念起初有点难以理解。毕竟,谁愿意在约束下编写程序?直到我看到Queneau的作品,才认为这个想法完全合情合理。

就像Queneau的故事一样,本书中的计算任务很简单:给定一个文本文件,我们希望生成文件中的单词列表及单词出现频率,并按词频降序打印出来。这个计算任务被称为词频(term frequency)分析任务。本书包含33种用于词频分析任务的编程风格,每章一种。与Queneau的书不同,我决定用语言描述每种风格的约束,并解释对应的示例程序。考虑到目标受众,我认为重要的是明确地提供这些见解,而不是让读者自行解读。每章都首先介绍风格的约束,接着展示示例程序,最后详细解释代码;大多数章节都有关于在系统设计中使用这种风格的额外介绍,以及编程风格的历史背景。历史很重要,一门学科不应该忘记其核心思想的起源。我希望读者有足够的好奇心,多多阅读“延伸阅读”部分的内容。

为什么是33种风格?之所以选择33种只是因为个人挑战。Queneau的书有99种风格。如果我的目标是写一本99章的书,我可能永远完成不了它!然而,作为本书基础的公共代码库可能会持续更新。这些风格分为九大类:历史、基础风格、函数组合、对象和对象交互、反射和元编程、逆境、以数据为中心、并发,以及交互。本书以分类方式组织内容,将彼此之间更相关的风格放在一起。当然,也可以选择其他的分类方式。

类似于Queneau的书,本书的编程风格练习也确实是练习。它们是软件的草图或分解,真正的软件通常需要针对系统的不同部分采用不同的(编程)风格。此外,所有这些风格都可以混合搭配,创造出更有趣的混合风格。

最后,尽管Queneau的书是这个项目的灵感来源,但软件与语言艺术并不完全相同。软件设计决策需要考虑效用函数,即对于特定目标,某些表达方式比其他表达方式更好[1]。在本书中,除非在某些明确的情况下,我会尽量避免做出好坏判断。之所以不能由我来做出这些判断,是因为它们在很大程度上取决于每个项目的上下文。