Dmitriy Gakh 著
Conmajia 译
原文发表于 2018 年 12 月 14 日 CODEPROJECT. 中文版已获作者授权.
主要讲述自动化代码生成的一般性概念,实现了两个可以写程序的机器人码农
第一个码农(简单,性能差)
第二个码农(复杂,性能好)
简介
为了满足不断增长的方法学需求和硬件性能,软件开发技术已经有了长足进步. 自动化在软件开发过程中对简化人工劳动起着越来越重要的作用. 软件开发自动化包含多个方面,例如代码构建管理器、静态代码分析器、自动测试等系统.
另一方面,人工智能和机器人方面的研究显著进步,机器人(bot)软件家族得到了蓬勃发展. 机器人们实现了预设的逻辑操作,只要硬件能跟上,它们的能力几乎没有限制. 而现代软件开发技术可以应用到机器人上,让它们具备可以编程开发的能力.
这篇文章介绍了创建软件开发机器人的一般概念,从现代软件开发的角度展示了这一概念. 文章还附带了两个机器人码农的例子作为演示. 写作本文是为了抛砖引玉,启发读者学习现代编程技术,并应用到现实生活中. 我只是初步阐述了自己的一点想法,读者中能人众多,希望能对此篇拙作加以斧正.
导读
本文两个主要章节:
读者可以从感兴趣的部分开始阅读.
术语
ATDCG,(Automated Test-Driven Code Generation)测试驱动的自动代码生成
SDB,(Software Development Bot)软件开发机器人
TDCG,(Test-Driven Code Generation)测试驱动的代码生成
TDD,(Test-Driven Development)测试驱动的开发
思路
软件开发的发展
引发 SDB 领域研究的最初问题是“软件开发技术是如何开发的?”,了解软件开发技术的趋势非常重要,这些知识有助于创造未来的愿景. 编程范式的前几个阶段可以简要描述为:
- 代码序列. 早期由于硬件能力较弱,一般的软件都是程序员单打独斗完成的. 这些程序通常是以非结构化的形式开发的.
- 结构化编程. 随着需要解决的问题涉及范围不断扩大,需要不断增加编程代码量. 为了更好管理代码,出现了分块组织的代码结构.
- 面向对象编程. 代码块结构不断增长,最终组合成一种新的称为对象的结构. 代码块之间关系的数量和复杂性也在增加. 引入了一些新的关系结构,比如封装、集成、多态等. 编程模型反映了复杂系统的特征,不同的单元执行不同的操作,和现实生活中的参与者及过程密切相关.
问题是:下一步是什么?编程方法朝着什么方法发展?
简化的 TDD 过程
TDD 是开发的进化方向,它结合了测试优先的开发,在开发或重构代码前编写测试. TDD 不是一种简单的编程技术,而是一种重要的敏捷需求和敏捷设计技术,其目标是编写干净而有效的代码. TDD 是解释本文主要思想的一种简洁方法. 本文以 TDD 作为进一步阐述的基础,给出了描述 TDCG 方法论的清晰路径. 完整的 TDD 过程包含代码重构阶段,通过测试确保代码正确. 在创建新代码阶段,不需要重构,可以简化 TDD 过程. 简化后的 TDD 主要有:创建测试,开发软件单元,可能重复开发任务,直到代码通过测试阶段.
几乎所有描述 TDD 任务的信息来源都关注到有的处理可以自动进行而减少人工工作这一事实. 其中尤为重要的一项特性是,自动任务运行的是经过证明的算法,性能稳定,但是人工执行的任务反而会引入错误. 换句话说,测试及软件的开发者有责任保证他们工作的质量. 故而考虑简化 TDD 过程时的主要任务可以从职责角度进行描述:
- 创建测试:影响整个软件开发单元的主要任务
- 运行测试:经过证明的自动过程,本身不会引入错误
- 开发软件:由程序员实施,通过测试保证其工作的质量
尽管本文考虑减少程序员对其工作的责任,但这意味着只有通过测试才能保证工作的质量. 为保持解释的简单性,暂不考虑其他质量指标,如代码可读性、开发时间、代码性能等.
测试驱动的代码生成
Raphael Marvie 在《测试驱动的代码生成简介》[Marvie, 2006]一文中描述了测试驱动的代码生成(TDCG)技术. Marvie 是在 TDD 处理的范畴内描述 TDCG 的,而本文将自动代码生成视为一种独立的技术,而不是 TDD 的一部分. 但是,所考虑的技术与 TDD 和其他信息技术有着密切的关系. 自动代码生成基于先前编写的测试,因此可以将其命名为测试驱动的自动代码生成(ATDCG).
上述推理表明,只有运行测试是自动化的,但创建测试和开发软件是 TDD 中的人工实施的任务(这些任务可以部分自动化,但不能完全自动化). 事实上,由于软件开发任务的质量是通过测试自动控制的,开发也可以完全自动化. 人类的角色将收缩为仅负责创建测试. 从业务的角度来看,这意味着程序员为软件单元创建、描述需求,而这些单元将根据需求自动创建. 因此,自动软件开发可以被看作是上述简化 TDD 过程的一种演变.
自动化测试
传统的自动化软件测试方法有一个称为代码覆盖率(code coverage)的特性. 这是用于描述在特定测试单元运行时,程序源代码的执行程度的度量. 较小的代码覆盖率意味着存在未经自动测试的代码. ATDCG 技术从零开始创建代码,因此可以保证测试完全覆盖所有代码. 换句话说,它有 100% 的代码覆盖率. 也正是因为这个特性,代码覆盖率无法用于衡量 ATDCG 成功与否. 于是,应该考虑引入新的指标,通过测试来衡量实际的业务问题覆盖率. 这一事实将成功与否的度量从软件开发领域转移到业务分析中. 关于这方面的话题已经超出了本文讨论的范围,不再赘述.
ATDCG 中测试的角色
创建测试是 ATDCG 过程中唯一需要人工执行的任务. 测试是软件单元自动生成过程的主要驱动,同时保证了过程的质量. 因此,ATDCG 中的测试具有以下特点:
- 整个 ATDCG 过程和结果的质量均取决于测试
- 创建测试是 ATDCG 最主要和最敏感的任务
- 与经典方法相比,创建测试与业务需求较创建软件代码的关系更为密切
- 需要研究、制定用于创建 ATDCG 测试的方法和规则
机器学习
正如前面提到的,ATDCG 开发的软件单元质量完全取决于测试. 反过来,测试的创建在很大程度上与业务需求相关,而不是与编程相关. 从另一个角度来看,测试将以声明性的方式描述,并包含大量的规则、数据. 因为测试创建过程与业务需求相关,所以这些数据应该具有足够高的抽象级别,以使测试创建者能够方便地根据业务需求开发测试. 与传统的测试方法相比,测试设计的高抽象级别和大量数据引入将导致复杂性的增加. 创建测试和运行 ATDCG 可以描述为:
- 以声明形式创建测试并考虑实际物理数据的数量
- 将创建的测试或其一部分视为软件单元自动创建的训练数据
这些观察结果表明 ATDCG 与有监督学习(supervised learning)有很深的关系,后者的相关理论和技术可以应用于 ATDCG 领域的研究.
社会影响
ATDCG 旨在完成软件开发任务的自动化,它将使程序员从这个角色中解放出来. 同时,测试分析师和创建者的角色成为成功运行 ATDCG 的关键. 此外,ATDCG 的开发显然将满足两条 Unix 编程规则 [Raymond, 2003]:
- 经济规则:程序员的时间是昂贵的,节省人的时间优先于机器时间
- 生成规则:尽可能编写一些程序,由它们负责编写其余的程序
因此,ATDCG 可以认为是一种软件开发技术,它可以带来更多的机会,为进一步研究这一问题提供机遇.
机器人码农
这里的机器人指的是软件机器人(software robot). 本节讲述两个 SDB 的创建过程.
- 1 号:最简单的 SDB,用于展示概念,性能较差
- 2 号:更好的性能,可以在 5-10 分钟内得到结果(Intel i7)
问题
本文试图给出一个尽可能简单的解释,以提出该技术的一般概念. 在这种情况下,最小的软件单元可以只是一个简单的函数,由一个写在高级语言上的代码行表示. 下面两个例子的目的都是基于训练数据在 C# 语言上创建一个简单的函数. 在每种情况下,测试将检查输入功能输出值的等效性.
训练数据
输入参数 (a) 和 (b),输出 (f) 在下表列出:
$a$ | $b$ | $f$ |
---|---|---|
38 | 2 | 1463 |
4 | 11 | 80 |
51 | 7 | 2645 |
12 | 32 | 313 |
6 | 21 | 150 |
20 | 38 | 599 |
25 | 43 | 849 |
3 | 19 | 113 |
40 | 34 | 1779 |
这些数据是用多项式 (f=a^2+5b+9) 得到的. 用这些数据检查创建的 SDB 是否正确操作. 在现实世界中,目标函数和测试很可能是未知的,并且非常复杂. 根据训练数据,机器码农理应找到与上述多项式相等的函数,但其结果可以有不同的形式. 在结果一节可以看到,机器人根据训练数据发现了多个版本的实现.
1 号机器人
前面说过,1 号机器人用于演示 SDB 的主要概念,它的性能较差,但胜在代码简洁. 1 号以 C# 语言创建 (f(a,b)) 形式的函数,产生类似这样的代码片段:
namespace Program
{
public class WorkingClass
{
public static int F(int a, int b)
{
return XXX;
}
}
}
机器人需要做的根据表 1 的训练数据,找到标为 XXX
部分的正确的 C# 语句,最后输出完整的程序. 在这个例子里,机器人码农在编程时将用到四个算术运算符(+
、-
、*
、/
)、两个变量(a
、b
)和 9 个数字(1
、2
、3
、4
、5
、6
、7
、8
、9
).
作为例子,1 号机器人程序实现的算法也很简单:
- 通过将从字母表中随机选择的符号插入到
XXX
位置来创建代码 - 把代码编译到 RAM。如果编译失败,回到 1
- 运行训练数据集函数
- 比较实际的函数输出和训练数据集的结果,如果有任何不同,回到 1
- 打印找到的函数
算法用 C# 实现,使用了反射、动态代码生成等技术.
性能
这种简单算法的性能非常之差. 对一个有 4 个操作和 5 个变量的程序来说,最多可以有超过 4100 万个不同符号组合,这对于简单的迭代而言计算量太大. 使用普通 PC 是无法胜任的. 就这个例子而言,我在配备 Intel i7 四核处理器的 PC 上花了 10 天时间运行 4 个机器人实例,才找到一个解决方案.
2 号机器人
1 号机器人可以用来理解自动创建软件代码的最简单算法,并研究所需的编程语言命令,但运行性能太差,不便于观察结果. 为了在合理的时间内得到结果,我对 1 号进行了改进,得到 2 号机器人.
2 号实现了遗传算法,大大减少了寻找解决方案的时间. 在 Intel i7 CPU 单核运行的情况下,2 号可以在 5-20 分钟内得到结果. 它的代码更复杂,不过我加入了不少注释,理解起来应该不算太难.
结果
下面的图片显示了由 2 号产生的几个结果,注意观察最终生成的 C# 代码和运行时间. 读者可以下载源代码并自己运行示例(参见使用代码一节).
未来发展
本文展示了创建 SDB 的可能性,但现在还不能用在实践中. 不过相关的技术如机器学习、数据挖掘、TDD、自动代码生成等都对它有所裨益. ATDCG 的未来发展可能涉及到编译程序和代码分析器的构建经验. 成熟的机器人可以创建软件单元,避免使用高级语言和编译阶段. 它们可以创建由中间语言(CIL)或 Java 字节代码(bytecode)组成的单元,这可以显著加快代码生成过程. 目前,有很多爱好者研究和开发 ATDCG. 理论研究将为理解 ATDCG 的优点以及如何在实践中加以实施打下基础.
代码使用说明 返回正文
1 号码农和 2 号码农开发、运行环境:
- 开发 Microsoft Visual Studio Community 2015
- 编译 Microsoft .NET Framework 4.5
- 平台 Microsoft Windows 8.1 64-bit on Intel i7
两个例子都使用了单线程,可以多开,但是 CPU 使用率会达到 100%. 在这样的高负荷下,CPU 会严重发热,长时间运行时要考虑到这一点. 2 号码农运行起来会快很多,CPU 负荷更低.
参考资料
- [Marvie,06] R. Marvie. An Introduction to Test-Driven Code Generation. EuroPython 2006.
- [Raymond,03] E. S. Raymond. The Art of Unix Programming. Addison-Wesley, 2003.
历史
- 2019-01-22 中文版
- 2018-12-15 更新了图片
- 2016-11-22 首发
许可
文中内容全部使用 MIT 许可.
关于作者
Dmitriy Gakh,来自阿塞拜疆 ,软件工程师、分析员、架构师、研究员、IT 应用专家、培训师.