基于众包问答信息的API使用代码搜索

2018-07-25 11:21李宇琨赵文耘
计算机应用与软件 2018年7期
关键词:离线帖子骨架

李宇琨 彭 鑫 赵文耘

(复旦大学软件学院 上海 201203) (上海市数据科学重点实验室(复旦大学) 上海 201203)

0 引 言

现代软件开发大量依赖类库以及第三方软件框架和开发,为此软件开发者经常需要寻找能够解决特定问题的API并通过示例代码学习相关API的使用方式。另外,我们在针对特定平台编程时,比如安卓平台、Windows平台等,我们还需要利用到平台提供的开发接口,以利用相应的硬件资源或者系统资源。例如,获取手机GPS定位、调用摄像头等。程序员在面对一个与API密切相关的编码任务时,他们需要知道哪个API能够帮助他们实现这个功能,这个API应该如何调用。目前,程序员主要依赖通用搜索引擎完成代码搜索,例如:百度搜索、Bing搜索。通过这类网站,程序员可以看到其他有经验程序员留下的文字以及样例代码,这些信息能帮助程序员理解相关API。但是,程序员很可能还不知道应该如何调用这些API,他们需要看更多的真实的代码。这时,他们会将相关API输入到Github、Bitbucket等开源代码托管网站,去搜索真实的示例代码。由于开源代码网站里面存在大量分支,分支与分支之间有大量克隆代码,导致搜索结果重复度较高。程序员如果在首页中无法获得正确结果,需要往后翻好几页,或者根据结果重新组织搜索词,极大地降低了工作效率,因此程序员需要代码搜索工具来提升搜索效率。现有的代码搜索工具大多利用API文档为数据源,通过匹配用户查询语句,返回相关API和与该API相关代码。例如,SNIFF[1]通过VSM(Vector Space Model)计算API文档与用户查询语句的匹配度查询相关API,并返回相关代码。CodeHow[2]通过使用扩展布尔模型改进匹配算法。但是,API经常被组合使用以解决相关问题,同一个API也会被用到不同的场景发挥不一样的作用。因此,通过匹配API文档与查询语句不能完全地理解用户的意图,也不能完全概括代码所包含的信息。最近几年,有许多研究者也在API的基础上增加其他信息,以更确切地匹配用户需求。例如,SWIM[3]通过对Bing的点击记录进行搜索,获取代码API结构序列,查询代码片段。DeepAPI[4]通过查询深度学习技术,利用API序列与代码注释信息,训练映射模型,将自然语言映射到API序列。但标准的代码注释信息较少,深度学习技术受到词表大小等因素限制,搜索范围不能太大。因此,我们通过观察程序员日常搜索代码过程,提出一个新的代码搜索方法。

本文首先接受用户输入的查询语句,再利用Stack Overflow中包含的大量含有样例代码的帖子,匹配相关的样例代码,抽取样例代码中的代码骨架信息,利用倒排索引技术,在代码库中搜索骨架相似的代码片段,返回给用户。然后利用Stack Overflow中包含的大量编码信息,通过文本抽取技术与代码骨架抽取技术,能更好地理解代码的语义以及用户的查询语义。为了测试我们工具的有效性,我们通过Github和Stack Overflow提供的源数据,构建了一个关于Java代码的搜索工具原型。该原型目前包含有700多万的Stack Overflow帖子,代码库中包含了3亿多行Java代码。用户可以通过网页使用该工具,从搜索框内输入问题描述,从结果页中获得代码骨架以及相应的代码片段。我们从相关代码搜索论文中选取了30个与API相关的问题进行测试,其中有8个问题,正确答案排名第一,有20个问题能在前5个结果中找到答案,有29个问题都能在前十个问题中找到答案。在返回的十个结果中,有40%的结果符合要求,其中有15个问题能找到不同类型的答案。

本文主要有以下几个贡献:

(1) 我们提出了一个从自然语言到API集合的映射方法。

(2) 我们提出了一个从代码库中抽取API与代码结构特征的方法。

(3) 我们提出了一个利用代码骨架(API与结构信息),搜索代码的方法。

(4) 我们为该方法实现原型工具,并进行实验,取得良好效果。

1 方法总览

图1展示了该方法的整体流程。该方法分成离线模块与在线模块。离线模块对Stack Overflow中的信息和Github中的代码进行离线分析,并建立索引文件,用于提升线上搜索模块搜索效率。离线模块主要分成三个部分:Stack Overflow文字摘要模块、Stack Overflow代码骨架抽取模块以及Github代码库代码骨架抽取模块。随后,我们对抽取出来的三个数据分别建立索引,并将索引文件更新至线上模块。线上模块则是利用离线索引文件,模拟程序员利用Stack Overflow搜索问题的流程,搜索出相关代码,并对代码片段进行评分与排名。线上模块主要分成两个模块:第一个模块为代码骨架搜索模块。该模块获取用户的查询语句,并通过离线建立的Stack Overflow的索引并获取相关主题的帖子,以及帖子中的代码骨架。然后,我们结合帖子中的文本信息分析判断不同代码骨架的评分,获取候选代码骨架,输入第二模块。第二模块为代码片段搜索模块。该模块主要接收代码骨架搜索模块的候选代码骨架,然后通过离线建立的代码库搜索相关代码,并根据代码相似度、代码骨架的评分等因素对代码片段进行排序,并将结果返回给用户,完成搜索过程。

图1 代码搜索流程图

我们通过一个搜索实例介绍工具的搜索流程。例如,输入How to read text file line by line,工具会利用离线模块建立的Stack Overflow索引,搜索与问题相关的帖子。图2是一个排名第一的含有样例代码的帖子,里面含有一片关于如何一行一行地读取文件的代码。通过代码骨架提取器,可以从中提取出如图3所示的代码骨架。同时,通过文本摘要模块,从帖子中抽取出line、file、read、append等十个词。同时工具还会搜索到其他一些关于文件操作的帖子以及相应的代码块。随后,将代码骨架放入至代码库中搜索,能搜索到如图4所示的代码。这是搜索到的如何一行一行读取文件的代码,相比于匹配的代码骨架,里面用Files.newBufferdReader()这个API代替了图3中的前两个API,同时多了Charset这个API。这片代码更为完整地读取了文件的代码,更贴切实际需求。

图2 Stack Overflow 样例帖子

图3 代码骨架

图4 文件读取代码

2 离线索引构建

离线系统是离线构建索引,并热更新至线上系统的模块。其中包括有代码骨架索引构建和文本索引构建。代码骨架索引构建分为Stack Overflow中示例代码骨架抽取和Github代码库骨架抽取。文本索引包括:文本摘要、文本向量抽取等部分。系统将索引文件热更新至线上系统,以保证系统稳定可扩展。

2.1 代码骨架抽取

API序列模式是由一系列经常在一起使用的API语句组合而成的序列。程序员经常会使用不同API序列模式在不同的环境中完成不同的编码任务。API的使用组合会形成一种模式,并与其适用场景存在一种对应关系[5]。Deep API[4]就是通过学习API序列与代码注释的对应关系,提供代码搜索的功能。但是传统的API序列往往会忽略代码中结构信息,仅仅记录代码片段中存在的API,这样会带来歧义。图2为关于逐行读取文件的代码。如果我们只提取API序列,BufferReader.readLine()后就不存在相关结构信息。但同样的序列模式,如果while语句改成if语句,那么就成了判断第一行是否为空的代码,语义完全不一样。因此,工具将在API序列模式中增加代码结构信息,在每条语句结尾按照代码层次结构依次加上结构信息,如图3所示。这样就能知道readLine这个API是在循环内部调用,可以尽量减少API调用模式的歧义。本文中,我们将这样含有代码结构信息的API序列模式称为代码骨架。代码中的结构种类较多,且包含的结构信息的API序列需要被快速搜索,因此,我们针对Java的语法特点,统一代码结构的形式。具体规则如下:1) 所有的API语句有A.B(C)的形式表示,其中A为API的类型,B为A中的字段或者方法,如果为构造函数则B与A同名,C为方法B的参数,如果B为字段则无括号。2) 每个API元素后面用中括号包裹,并从左往右依次带有结构信息。3) 所有的循环结构用while表示,并将判断条件放置在第一条。4) 所有的判断结构全部用if表示,包括else分支同样用if表示,将判断条件放置在第一条。5) Try-catch语句,将try后括号内部的代码放入try内部第一句,catch语句的标签由括号内部的异常类型的名字代替,finally语句后缀添加finally标签。6) 内部方法、匿名方法等直接当成新的代码片段处理。经过上述规则限定,我们能统一代码骨架形式,下面是抽取代码骨架的具体实现。

对Stack Overflow中样例代码进行解析。Stack Overflow中含有大量无法解析的代码片段,不能通过传统的AST解析器解析其中代码片段[6]。因此,工具利用字符串匹配以及正则表达式匹配的方式逐句匹配。首先,根据Java的语法规则,利用“;”、“{”、“}”三个符号对Java语句进行分行,去除代码注释,并逐句匹配。然后通过Java关键字匹配结构信息(while、if等),如果匹配则将结构信息存入栈中,如果不匹配,则通过正则表达式匹配去语句内容。其中正则表达式主要使用以下3个公式[7]:

1) 类型声明:typeVariable=/(w+(?:..w+)?)s([a-z]w{0,})/g

2) 方法调用:methodCall=/(w+)。(w+)(/g

3) 参数匹配:argsRegex = /((.*?))/

同时,使用键值对记录变量类型与变量名的对应关系,并用双端队列记录已成功确认的代码序列信息,以上代码主要针对Stack Overflow中的示例代码。相比于Stack Overflow中的示例代码,Github中工程代码含有更多的上下文信息,比如有完整的类,类与类之间有方法调用等,但为了与Stack Overflow中提取的代码骨架信息保持一致,我们依然使用正则表达式匹配的方式进行解析。但是,额外增加了两个要素:1) 由于Github中代码骨架抽取以方法为单位,因此,这里增加全局变量类型匹配,建立全局变量名与变量类型的映射关系。2) 根据Java编码规范[8],包名应该有域名、公司名、项目名以及包所属模块共同决定,因此可以通过分析包名与引入类路径,判断是否为引入外部第三方库。具体方法为将包路径和引入的类型路径分词,计算两个路径的词向量夹角的余弦值,大于0.5的为内部引入,小于0.5的为外部引用,并将外部引用更新API候选类型。

2.2 Stack Overflow信息索引构建

Stack Overflow网站提供了一个通用的搜索接口,但是使用通用的搜索接口无法获取每个帖子的评分。虽然有标签选择,但是无法完全保证帖子的代码语言,也无法保证搜索结果的质量。因此,我们选择通过Stack Exchange提供的Stack Overflow数据,离线建立索引。同时能对帖子进行清洗,对内容做预处理,保证线上搜索速度。

Stack Overflow作为程序员的问答网站,不但包含有大量有意义的回答与代码,同时也存在许多缺陷,比如:问题语义重复,回答中没有答案等。同时,有些冷门的问题虽然有正确答案,但是缺少其他人浏览和认同,说明这是一个不普遍的问题。因此,我们首先要对Stack Overflow进行清洗。清洗的规则如下:1) 含有Java或者Android标签,因为工具主要搜索Java代码。2) 含有提问者认同的标准答案。3) 问题或者标准答案的赞同数要为2或者2以上,以保证问题的普遍性。通过帖子清洗之后,将帖子的标题、问题、标签以及最佳答案四个部分组合成最终数据,并删除其余答案。然后,我们将剩余的帖子分成两个部分:一部分包含样例代码,另一部分不包含样例代码为纯文字。

对于含有样例代码的帖子,我们需要从含有代码的帖子中抽取出样例代码。我们针对部分Stack Overflow帖子进行统计分析,并调研了之前与Stack Overflow相关的文献[9-10],总结出以下三个特点:

1) 代码格式特征。Stack Overflow会利用

的html标签块,将大块的代码片段包裹起来,利用标签将小块代码元素包裹。这样做一方面可以保持代码的缩进、换行等格式,方便读者阅读。另一方面,
标签由于能保持输入格式,因此也有非代码元素出现在其中,例如:错误信息、配置文件、脚本文件等。2) 代码位置特征。Stack Overflow上代码出现在帖子的位置随不同作者的习惯会出现在不同的地方。有些样例代码会出现在文本末尾,有些样例代码会出现在帖子开头,还有一些代码会出现在文本中间。3) 代码内容特点。部分含有代码的帖子中问题与答案中都存在代码,但大多数情况问题中的变量名与答案中的变量名具有同样的含义。

针对Stack Overflow中代码存在的特征,我们决定采用以下方式对帖子中代码进行提取:

1) 提取帖子中所有被

标签包裹的内容,并通过启发式规则检验其是否为Java代码。主要启发式规则为:(1) 通过匹配以“at”开头的语句的数量判断是否为Exception输出。(2) 通过匹配<>判断是否为xml。(3) 至少有一个“.”符号出现,保证存在方法调用。(4) 通过查询var的位置判定是否为JavaScript代码,如果含有以var为类型的变量,则判定为JavaScript代码。

2) 将所有检验为Java代码的片段拼接并通过语句级别的去重,形成一片无重复代码片段,并放置到代码骨架抽取器中抽取代码骨架。然后把代码骨架抽取器中抽取出来的骨架与原帖子建立映射关系,并存入数据库便于搜索。

针对纯文本帖子,我们需要做的是对文本进行过滤,排除一些常用的词,或者一些感谢的词。文本过滤的步骤如下:(1) 将标题直接作为有意义的文本。(2) 将被标签标记的代码名词直接算作特殊文本。(3) 将剩余的文本根据句子组成,形成一个网络,然后通过PageRank算法[11]计算每个词的结构分,并且句子中所有词的平均分作为该句子得分,去除掉得分为1以下的句子。(4) 将剩余的文本作为特殊文本。最后将特殊文本作为数据源,建立搜索索引。

2.3 代码库信息构建

通过对Stack Overflow信息提取,我们可以将用户输入的查询语句转化成代码骨架。得到骨架之后我们需要通过对代码库进行处理,搜索实现该代码骨架的相关代码。为此,我们通过Boa[12]获取大量Github上开源项目地址,选取其中Java相关的代码工程进行解析,抽取开源代码骨架并建立索引。

Github是开源代码托管网站,大量程序员在上面分享自己的代码,并浏览他人代码。还有大量程序员会在遵守开源协议的基础上,克隆他人代码进行二次开发。因此,Github中存在大量克隆代码。克隆代码的存在会导致大量重复代码骨架被抽取,建立重复索引,使最终搜索结果单一化。但是,由于代码量较大(约16 GB),通过代码克隆检测的方法难以在如此大量的代码中高效地检测出克隆代码。为此,我们根据数据特点,设计了一个近似解决方案,从两个维度对重复代码进行剔除:一是线下模块,通过文件级别的检测去除克隆文件;二是线上搜索排序模块,在方法级别剔除克隆代码块,该模块会在后文提到,此处暂时不谈。线下模块主要针对文件级别的克隆。由于Github中存在大量开源代码的二次开发工程,因此在文件粒度上有许多文件的内容是完全一致的。所以,我们决定通过文件级别检测。但代码文件数众多,为了保证检测运行效率,我们通过文件大小对文件进行分片。以1 KB为间隔,将代码文件划分到不同的区间,随后通过MD5算法对文件进行签名,然后利用数据库存储与查重,同时对数据进行持久化,方便后续扩展新代码文件时,能继续查重。最后删除重复文件,并放入代码骨架抽取器中抽取代码库代码骨架。最终将代码骨架与代码建立索引,方便搜索。

3 在线搜索

离线索引构建完成后,在线搜索模块会基于离线索引为用户提供搜索服务。为了保证搜索的效率及可扩展性,我们采用倒排索引技术[13]完成索引构建以及搜索。倒排索引是通过对搜索数据进行分词,建立单词到源数据间的联系,同时通过对分出的单词合并成前后缀字典树,压缩存储空间,增加索引效率。通过倒排索引,可以将离线完成的索引文件热更新至在线搜索系统,同时也能高效率地使用空间向量模型对文档进行评分与排序,方便后续排名。但是,仅仅依靠文本匹配搜索效果难以完全理解帖子的含义,因此,我们将根据不同帖子出现不同API出现的次数,以及帖子中代码骨架与代码库中代码骨架的匹配度进行新的评分。接下来,我们在倒排索引结果基础上进行的二次评分与排序。

3.1 代码骨架搜索

首先将用户的查询语句放入Stack Overflow搜索模块中搜索其对应的代码骨架。在构建Stack Overflow搜索模块时,分成了两个搜索模块:1) 含有样例代码的帖子的搜索,选出代码骨架候选集。2) 纯文本帖子的搜索,提取关键词,为代码骨架进行评分。具体步骤如下:(1) 利用搜索工具从含有代码的帖子中搜索出排名前十的帖子,搜索匹配分即为代码骨架基础得分,另外从只含有文本的帖子中搜索出排名前十的帖子。(2) 代码骨架之间两两对比,合并完全覆盖的代码骨架。(3) 对所有文本中出现的词进行合并与计数,并以次数最高的词为1分,其余的得分为其出现次数与最高次数的比值,并与代码骨架中出现的词对比,如果代码骨架中出现该词则增加词相应的分数。(4) 用户输入的查询语句中的词全部算作1分,与代码骨架匹配,计算相应得分。所有剩余代码估计排名,选取前3个最终代码骨架放入代码库中搜索代码骨架实验代码。

3.2 代码片段搜索

代码库的搜索是基于代码库的搜索。同样,我们通过倒排索引对3个候选代码骨架进行搜索,每个候选骨架得到排名前20的实现代码片段。由于之前只在文件级别做过去重,还有大量的代码块克隆,或者开发者针对代码文件有扩展但遗留许多方法依然相同。因此,这一步搜索依然会得到许多克隆代码。我们需要在线上搜索系统对结果进行进一步去重。这里代码量相对较少,但是现有的代码克隆检测工具没有提供API调用,我们需要重新建立新的进程进行克隆检测,这样加重了线上系统负担。另一方面,我们也需要去除一些含有微小变化的代码,是返回结果更加多样。因此,我们采用空间向量模型,把代码分成一组词向量,计算代码之间的夹角余弦值,将余弦值大于0.9的代码结果合并,但是合并会为这个片代码增加一个(1+n/10)的系数,其中n为合并代码段的数量,说明这样一片代码会有更高的流传度。最后将代码得分与代码骨架得分相乘作为代码片的得分。系统最终会选出分值最高的10片代码,为保证这10片代码中含有不同类型的代码骨架,系统将降低同一代码骨架下排名靠后的代码片段的权重。每个代码片段权重将乘上一个(1-x/10)的系数,其中x表示代码片段在其代码骨架下的排名。这样排名第一的代码片段获得所有分数,排名最后的代码片段不得分,其余代码片段按照比例计算。最后对所有代码片段的权重进行排序,选出前10个代码片段,并加上所使用的API,返回给用户,完成整个搜索过程。

4 实 验

本节我们通过网页对外代码搜索服务,并以此为基通过实验检验工具搜索结果的正确性,同时计算搜索时间保证工具实用性。

4.1 准 备

我们通过Stack Exchange上面的提供的公共接口,下载了2014年至2016年的所有帖子数据,在利用Java和Andorid两个标签过滤之后,一共得到700万左右的帖子,其中含有样例代码的帖子总共137万左右。我们利用Stack Overflow离线索引模块对帖子进行解析,并借助Java中的开源倒排索引库Lucene[14]建立相关索引,最终得到1.1 GB索引文件。同时,我们利用Boa提供的开源软件仓库的地址,以及Github提供的公共接口,爬取了Github上面3亿行Java代码代码,里面包含有2 786 631个代码文件,占用磁盘空间为16 GB,经过文件去重之后,文件数减少为2 132 218个,占用空间为12.8 GB。在这个基础上,我们借助代码索引模块解析所有代码文件,接入代码骨架的分词器,并用Lucene建立索引,最终得到2.1 GB索引文件。两个索引库都留有更新接口,方便我们可以将新的帖子数据或者代码数据热更新至线上系统,改进搜索效果。

为了验证本方法的搜索准确率与搜索效率,我们从近3年的4篇关于代码搜索的论文中抽取出30个与API关系较为密切的问题,问题涵盖了字符串处理、文件读取、数据库操作、网络编程等多个应用场景,答案中既含有单API问题,又包含有多API组合完成的问题。问题如表1所示。

表1 查询列表

4.2 实验结果

我们请求了2位有5年Java编程经验的硕士对所有问题返回的十个答案进行验证,判断每个答案是否能解决相关问题。两位同学独立验证所有答案,遇到有分歧的答案将交由第三位有5年Java编码经验的博士进行验证,最终确定标准答案。

实验结果如表2所示。Id表示问题的序列号,与表1相对应,FRank表示前十个答案中第一个出现正确答案的选项排位,-1表示没有搜到正确答案。从该栏结果表明,有40%的问题能直接在第一个结果中找到正确答案,只有一个问题无法找到答案,基本能满足程序员搜索到问题的需求。%Top5指的是前5个答案中正确答案的比例,%Top10表示的前10个答案中正确答案的比例。两者整体正确率偏低,可能是因为搜索程序本身难以理解一些细节化而选择将更多的同类型关键字加入,从而匹配到同一类的问题。并且在最后选择代码片段的时候为保证解决方案的多样性,系统降低同一代码骨架下排名靠后代码的权重有递减,以保证多个代码骨架的出现。这个设计导致准确率偏低,但是大幅提高召回率,使得大多数问题能搜索到正确答案。另外,前十个正确答案个数不显著高于前五个正确答案个数,这也说明了排序算法的正确性,能基本保证正确的答案整体靠前。Time表示的是搜索代码所用时间,时间通过Web前端脚本计算,单位为s,结果取第二位小数。服务器部署在本地电脑,处理器为I5-5200U,2.2 GHz,内存为8 GB。搜索时间的范围在1.34~1.91 s之间,平均值为1.62 s。整个搜索的响应时间较短,不会给用户使用带来不良体验。

表2 实验结果

4.3 实验案例

我们现在来看几个搜索案例,根据搜索案例查看代码搜索工具在处理不同类型问题的中间结果,以及针对不同问题的不同效率。首先查看一个正面样例,generate md5 hash code。我们通过搜索这样一个语句,找到问题13201180,标题为Issue with MD5 hash generation。内部有一段代码使用MessageDIgest类对字符串进行MD5加密。代码骨架抽取器从样例代码中抽取到合法的代码骨架,并且凭借代码内部变量名以及MD5文本出现次数得到较高评分。通过这片代码骨架,系统可以搜索出相关代码片段。另外,由于部分其他帖子的干扰,例如问题5992778,Computing the MD5 hash of a string in scala。这个问题打上了Java标签,但是问题却与scala联系更密切,所以搜索结果中混淆有错误API骨架,例如MD5.asHex等。但是,由于大部分帖子与Java相关,且代码库中MD5这样的API较少,因此,在排名上面正确的答案依然能占据主要地位。

其次,我们再来看我们无法找到正确答案的问题,how to save image in png format。首先,通过对Stack Overflow帖子的搜索,我们能获取到一些含有代码的帖子,标题如下:1) java save jpg as png。2) How to get the format(ex:jpen,png,gif) of image file (BufferedImage) in java。3) How to save optimized png images with java’s ImageIO。4) Save a GIF with index transparency using ImageIO等。大部分帖子内容关于ImageIO如何写入图片。我们也能从中找到关键API,例如:ImageIO.write(image, “png”, new File())。但是,这里有特别关键的字段png,是以字符串的形式传入,而系统在提取相关形式的代码骨架的时候,这个关键字已经被模糊化成了String。因为大部分代码的字符串是表示特定项目背景的,无通用借鉴意义,因此我们无法搜索到特定情况下的API。我们只能搜索到图片存储的一般意义上的二代码,没有特定指明为“png”格式。我们尝试将结果集扩大,能找到一些以“png”为参数的代码片段,但是这存在偶然性,我们方法确实缺乏处理有意义的字符串的能力。如果需要解决这类问题,未来可以考虑设立二级匹配模式,将我们认为不重要的信息,例如字符串常亮等建立二级索引,在一类索引的基础上对二类索引进行搜索,以解决这样的问题。

最后,我们查看一个排名靠后的问题,test file exists。这个问题答案出现靠后主要是因为问题过于简单,只需要file.exists单个API就能解决。但是file.exists这样一个API常常与其他API连动,比如:如果文件不存在就创建一个,如果没有该文件就终止程序等。因此,在搜索帖子的时候会得到很多与其他文件操作相关的API。且其他相关的API出现次数又会比file.exists出现次数更多,因此权重反而在这个API上面。因此,这样在实际代码中单独使用次数较少,也不便于我们进行代码搜索工作。当然,这也是大多数依靠信息检索实现搜索的一个问题,难以确切理解题目意义,只能靠文本匹配度以及关键词出现次数,确定结果权重。不过,总的来说,我们的搜索方法能找到与问题相关的帖子,并能通过解析帖子中存在的代码,匹配到代码库中,并最终返回给用户正确的结果。

5 相关工作

代码搜索一直是软件工程领域的热点话题,研究者先后提出了各种各样的代码搜索方法。这些方法分别支持不同类型查询方式,例如输入自然语言查询,或者输入代码语言查询,还有通过正则表达式查询等。其中,通过自然语言搜索代码与通用的搜索引擎相似,实现难度也相对较大,因此有更多的研究集中在通过自然语言搜索代码上。

许多国内外学者尝试使用各种方法帮助程序员搜索相关代码。早期,研究人员将代码文本当作普通文本对待,引用信息检索领域中的相关技术,通过关键字或者正则表达式搜索相关代码。此类搜索适用于程序员搜索内部项目的代码,需要程序员熟悉项目内容,这其中的代表工具有Ohloh[15]、Krugle[16]、Sando[17]。

随着传统机器学习技术的发展,许多工作通过将软件开发过程中的其他制品与代码关联,实现自然语言到代码语言地转换。在这其中,Strathcona[18]通过记录项目的开发文档,分别项目的开发层次,为用户提供不同具有类似功能的工程以及代码片段。Sourcerer[19]则是基于Lucene将更大范围的代码库以及代码间结构信息存储起来,用户可以通过代码关系结构搜索代码。随后,代码API文档越来越完善,文本分析技术越来越强,有的研究开始利用API文档作为搜索资料,以更好地理解使用了该API的代码。其中,SNIFF[1]通过简单的空间向量模型,对用户的查询与API文档进行匹配,获取单一API的评分,选取最后结果。而CodeHow[2]则在此基础上,利用扩展布尔模型,结合条件概率改进其在多API匹配的效果。但是,API文档质量不高,因此,后面有更多的学者将研究方向转向众智平台,利用Stack Overflow、Bing等通用编程知识搜索入口,改善代码搜索结果。QECK[20]利用Stack Overflow平台中已有数据,通过半监督训练扩展用户查询语句,提高搜索效率。SWIM[3]则是利用Bing搜索引擎,搜集与程序相关帖子,通过抽取代码结构序列匹配代码库。本文与SWIM的主要不同点在与以下几点:1) 本文合并同类型帖子并加重其综合评分,同时区别不同的解决方案。2) 本文提取的代码骨架相比于结构序列更为精简,同时适用于无法编译的网页中的代码段。3) 本文为适配代码骨架搜索,重新建立代码库。随后,RACK[21]利用Stack Overflow与Github,并结合IDE(代码编辑器)中的上下文环境,实现代码搜索。但是相对于本文,RACK没有利用代码结构特征,紧紧利用Stack Overflow将用户的查询语句转化成相应的API,并通过Github提供的接口进行代码搜索,没有对代码进行预处理,建立相应的代码库。

近年来,随着深度学习的发展与在软件学科的应用,DeepAPI[4]利用循环神经网络,将API序列与相关注释作为训练样本,最后利用训练出来的循环神经网络,将自然语言映射到API序列,展现出相同API序列的代码片段。深度学习能更好地理解用户输入的查询语句,以及自然语言与代码的对应关系。但是深度学习技术词表有限、搜索范围不广、可扩展性不强。

在国内,也有许多研究人员正在研究代码搜索课题。DERECS[22]通过挖掘开源网页中,收集代码片段与解释之间的对应关系,扩展搜索语句,最终搜索相关代码。顾逸圣等[23]通过结合代码结构与查询语句之间的关系,直接将查询语句映射到代码语句。

6 结 语

本文介绍了一种通过搜索众包问答信息将自然语言转化成代码骨架,并通过自己建立代码库,将代码骨架映射成代码片段的代码搜索方法。通过实现一个原型工具,来测试方法的准确性与时效性。实验中有40%的问题的正确答案在排序的第一位,其余的问题大部分能在前5个找到正确答案。同时,工具的响应时间较快,不给用户带来太多负担。

本方法代码搜索速度较快,但是在语义理解上有较多欠缺。未来可以通过深度学习等技术,深入理解用户语义或者论坛中的文本语义,甚至是代码语义,从而实现更精确的代码搜索工具。

猜你喜欢
离线帖子骨架
基于卷积神经网络的离线笔迹鉴别系统
浅谈管状骨架喷涂方法
异步电机离线参数辨识方法
新版Windows 10补丁离线安装更简单
骨架密度对炭/炭多孔骨架压力浸渗铜的影响
周博士考察拾零(六十六)日光温室前屋面开机具作业门处骨架的处理方法
暴力老妈
博泽引领座椅骨架技术发展
高手是这样拍马屁的
离线发文件 不是会员也能用