下载本文的 PDF 版本 PDF

计算机程序中的意义和上下文

在程序员之间使用源代码作为媒介共享领域知识

阿尔瓦罗·维德拉

当您查看函数程序的源代码时,您如何知道它的含义——也就是说,这个函数代表什么对象或过程?意义是在函数的返回值中找到,还是位于函数体内部?函数名又如何呢?

回答这些问题对于理解如何使用源代码作为媒介在程序员之间共享领域知识至关重要。无论是调试程序还是向程序添加新功能,程序员都必须阅读代码以了解程序正在做什么。通过阅读,程序员还必须了解问题领域在代码中是如何表示的,这样他们才能确定对源代码的更改不会使程序以意想不到的方式工作。

编程往往发生在团队中,并且可能会在正在进行的项目期间向团队添加程序员。新程序员应该通过阅读代码来理解程序在做什么。对于独自工作的程序员来说,当过一段时间后回到源代码时,该程序员必须理解他们过去写了什么。在任何情况下,程序都是程序员之间交流以分享其解决方案的媒介。

正如格言所说,“程序必须首先为人编写,其次才是为机器执行而编写”,1那么,这种共享的意义应该在程序中的哪里找到呢?

 

函数和方法名称

假设我们有一个名为 Animal 的类,它有一个名为 getName 的方法。该类在迭代中被使用,如下面的 forEach 循环所示

 

class Animal
    public getName()
    {
        return name;
    }
}
 
animals = loadFromDatabase();
forEach(animal in animals) {
    print(animal.getName());
}

 

此方法可以返回宠物的名称(例如,toto)或物种名称(例如,cat)。运行此程序将产生诸如 horsecatdog 之类的输出,这将帮助您推断该方法正在返回物种名称。(您可以争辩说,该方法的更好名称是 getSpeciesName(),这是一个有效的批评。)

getName 方法呈现了解释上的歧义,可以通过查看方法的返回值来解决。但是,您仍然不能确定您处于正确的轨道上,因为您不知道需要多少种情况才能做出正确的推论。代码中是否有其他设备可以帮助理解含义?为了回答这个问题,假设您继续浏览此项目的代码,直到您惊讶地发现另一个文件,其内部的代码与 Animal 文件完全相同。这是一个重复的错误,还是发生了其他事情?在执行新发现的代码几次后,您看到它返回诸如 unicornmermaid——虚构动物之类的值。检查这两个文件的文件夹结构,您发现最后一个文件位于文件夹 lib/animals/fictional 中,而第一个类位于 lib/animal/non-fictional 中。因此,源代码外部的一段上下文——一个副文本——正在帮助解释。3

副文本是一段信息,例如书名、章节标题或序言,指示如何解释文本,因此即使您想认为《堂吉诃德》可能是一部历史纪事,但它在书的前页中被描述为“小说”这一事实告诉读者它不能被视为事实。

就源代码而言,虽然 unicornmermaid 的返回值可以帮助您推断函数的含义,但文件夹结构可以提供更强有力的信息,说明该方法的含义——例如,揭示该项目正在处理两种动物,虚构的和非虚构的。(Java 和 C# 等编程语言在语言级别支持此功能,分别称为包或命名空间。)

 

返回值

函数的返回值可以被信任为指示函数的作用以及其可能的含义吗?考虑以下函数

 

function square(x) {
    // 此处进行实现。
}
square(5) // 返回 25

 

(双斜杠表示代码注释——即,编译器将忽略的代码,因此不会执行。)

 

名为 square 的函数,当提供整数 x 时,将其提升为 2 的幂。在本例中,当提供 5 时,该函数返回 25。到目前为止,一切都很好。如果检查函数的源代码发现以下内容,会发生什么?

 

function square(x) {
    return 25;
}

该函数返回硬编码的 25,因此如果您传递 5 或 -5,该函数将按预期工作。函数返回值可以被视为指示性符号——也就是说,它们表明函数已执行,但它们无法告诉您任何关于函数是否按预期工作的信息。这就像在雪地里看到脚印。您可以假设一只野兔刚刚穿过森林,但您不能 100% 确定它,仅仅基于脚印,因为它们可能是某人为了恶作剧而放置的(请参阅翁贝托·埃科关于指示性符号的著作2)。

同样,返回值不足以推断函数是否正确工作——即使可以将函数的返回值与您可能理解为函数含义范围内的已知正确值进行比较。在 square 的情况下,您可以编写一个测试程序来检查,如果提供值 3,该函数返回 9,如果提供 -5,它返回 25,等等。这变得同义反复:因为您假设函数 square 实现数学指数运算到 2 的幂,所以您就假设 9 和 25 是正确的返回值。

与之前的 Animal.getName() 示例一样,这些返回值表明 square(x) 不是在屏幕上绘制边长等于提供的 x 输入值的正方形的图形函数。然而,函数 square(x) 返回整数还是在屏幕上绘制正方形,可以从函数返回值的类型推断出来,假设该信息可以在程序的源代码中指定。某些编程语言(例如 Haskell 或 Java)允许程序员为函数提供类型定义。因此,在之前的情况下,您可以编写类似 square(x:Integer) -> Integer 的内容,这意味着函数 square 接受一个整数参数 x 并返回另一个整数——引导您推断或假设它返回 x 的平方。

从返回值中,您可以查看函数的实际主体,因为您可以在那里了解正在实现的算法类型。

 

函数体

下一个任务是理解是否所有返回值都是相同的。假设您有函数 random(start:Integer, end:Integer) -> Integer——也就是说,该函数返回一个随机整数,该整数落在 startend 输入参数指定的区间之间。问题是,您仍然不知道您从该函数接收到哪种随机数。

从安全的角度来看,当使用 PRNG(伪随机数生成器)时,您可能想知道用于生成它们的算法,因为有些算法对于加密应用程序来说或多或少安全。此信息在函数体中找到,您可以在其中看到函数内部实现了哪种类型的算法。因此,如果您有两个 random 函数,它们具有不同的实现,一旦执行,它们都恰好返回数字 7,那么您可能想知道这些数字是否来自诸如唐纳德·克努特4提出的线性同余法生成的序列,还是来自盖伊·斯蒂尔5提出的序列。

类似的示例可以在名为 sort(List[Integer]) -> List[integer] 的函数中看到,该函数接受类型为 Integer 的值列表,并返回排序后的 Integer 列表(升序还是降序现在并不重要)。要了解使用了哪种排序算法,您必须查看函数的源代码,您可能会在其中找到快速排序或插入排序算法的实现,仅举几个例子。

程序中的意义不仅存在于程序源代码的各个部分——函数名、函数参数、函数体——而且还存在于包含该函数的包名,以及每次函数执行接收到的函数类型的各种标记、返回值。每个随机数都是一个标记,揭示了随机算法正在生成的序列类型。

 

结论

程序员可以从这些信息中做些什么?理解代码不会“自己说话”,但是代码内部和外部都有各种位置可以引导解释。这篇短文无法提供关于如何编写更易于阅读或理解的代码的指南,但它可以建议关注此处提到的代码的每个部分或方面可以指导关于使用源代码来传递关于问题领域的信息的决策。这为未来接近代码的开发人员提供了许多扶手,以指导他们解释代码。他们不会只找到一些代表模型的随机词,还会找到这些词有意义的上下文。

未来的文章可以探讨程序内部名称(函数名、变量名、类型名等)中使用的词语之间的关系,并解释它们是如何被用来构建某种词汇表或 DSL(领域特定语言),代表现实世界的某些过程——很像超市库存的工作方式。这可能有助于理解程序员需要哪些能力才能理解程序的作用。本文将其探索限制在查看信息可能存在于程序中的位置,而不是从语义的角度来看它是如何产生或使用的。

 

参考文献

1. Abelson, H., Sussman, G. J., with Sussman, J. 1985. 计算机程序的构造与解释. Cambridge, MA: MIT Press.

2. Eco, U. 1979. 符号学理论. Bloomington, IN: Indiana University Press.

3. Genette, G., 2001. 副文本:解释的阈限. Cambridge, England: Cambridge University Press.

4. Knuth, D. 2011. 计算机程序设计艺术,第 2 卷. Boston, MA: Addison-Wesley Professional.

5. Steele, G., Lea, D., Flood, C. H. 2014. 快速可分割伪随机数生成器。载于 面向对象程序设计系统语言和应用国际会议论文集,453-472; https://dl.acm.org/doi/abs/10.1145/2660193.2660195.

阿尔瓦罗·维德拉是微软的开发者倡导者,并组织 DuraznoConf。他是《RabbitMQ 实战》的合著者,并曾为 撰稿。他的 Twitter 账号是 @old_sound

版权所有 © 2021,所有者/作者持有。出版权已许可给 。

acmqueue

最初发表于 Queue 杂志,第 19 卷,第 5 期
数字图书馆 中评论本文





更多相关文章

凯瑟琳·海耶斯,大卫·马龙 - 质疑评估非加密哈希函数的标准
尽管加密和非加密哈希函数无处不在,但在它们的设计方式上似乎存在差距。出于各种安全要求,存在许多用于加密哈希的标准,但在非加密方面,存在一定程度的民间传说,尽管哈希函数历史悠久,但尚未得到充分探索。虽然针对真实世界数据集的均匀分布非常有意义,但在面对具有特定模式的数据集时,这可能是一个挑战。


妮可·福斯格伦,埃里尼·卡利亚姆瓦库,阿比·诺达,米凯拉·格雷勒,布莱恩·豪克,玛格丽特-安妮·斯托里 - DevEx 实战
随着领导者寻求在财政紧缩和人工智能等变革性技术的背景下优化软件交付,DevEx(开发者体验)在许多软件组织中越来越受到关注。技术领导者凭直觉接受良好的开发者体验可以实现更有效的软件交付和开发者幸福感。然而,在许多组织中,旨在改进 DevEx 的拟议倡议和投资难以获得支持,因为业务利益相关者质疑改进的价值主张。


若昂·瓦拉霍,安东尼奥·特里戈,米格尔·阿尔梅达 - 低代码开发生产力
本文旨在通过介绍使用基于代码、低代码和极限低代码技术进行的实验室实验结果,研究生产力方面的差异,从而为该主题提供新的见解。低代码技术清楚地显示出更高的生产力水平,为低代码在短期/中期内主导软件开发主流提供了强有力的论据。本文报告了程序和协议、结果、局限性和未来研究的机会。


伊瓦尔·雅各布森,阿里斯泰尔·科克伯恩 - 用例至关重要
虽然软件行业是一个快节奏且令人兴奋的世界,其中不断开发新的工具、技术和技巧来服务于商业和社会,但它也健忘。在其快速前进的过程中,它容易受到时尚潮流的影响,并且可能会忘记或忽略一些永恒问题的成熟解决方案。用例于 1986 年首次引入,并在后来普及,是这些成熟的解决方案之一。





© 保留所有权利。

© . All rights reserved.