和曉健 彭 鑫 趙文耘
(復旦大學軟件學院 上海 201203) (上海市數據科學重點實驗室 上海 201203)
開發人員在論壇與博客中經常分享自己的編程經驗,或者提出在程序開發運行中遇到的問題,尋求專業人員的幫助與解答。Stack Overflow作為世界上最大的軟件開發問答網站,目前已經有來自230多萬注冊用戶的超過1 700萬帖子討論關于編程語言、操作系統、數據庫、算法等各類與程序設計相關的問題。如果能夠挖掘網站中包含的軟件開發知識,將給后續的研究帶來巨大幫助。
要抽取出開發人員討論中的有用知識,就需要有效的技術識別出討論文本中的計算機軟件領域實體。實體識別屬于自然語言處理中的關鍵性基礎任務,在通用領域的文本中對人名、地名、機構名、日期、時間等的識別已經有了成熟的技術并達到了較高的準確度。但是由于語言習慣以及個人偏好等方面的差異,開發人員在問答網站中提及某個計算機軟件領域相關實體時,往往將它以純文本的形式混雜在自然語言文本中,而且經常使用非規范化的表示,從而影響了其他開發人員的閱讀并且妨礙了自然語言處理程序對文本的自動化分析,最終造成了知識抽取的不準確。
API是編程語言中極為重要的一個部分,它封裝了某個功能的實現細節,使得開發者只需調用該接口就能快速方便地編寫程序而無需了解其具體實現。然而關于問答網站中開發人員對于API的討論情況,如API在文本中所屬的句子成分以及API是在什么情況下被討論的研究還比較缺乏。所以為了幫助后續研究者更好地聚焦開發者在討論不同API時所關心的問題,以及更好地構建自然語言處理程序解析軟件領域的文本,同時考慮到Java API與Android API作為目前流行的智能設備中廣泛使用的編程語言類庫,所以本文選擇對Stack Overflow中包含這兩類標簽的帖子進行分析。通過收集當前流行版本中包括的Java與Android API,并且使用一些規則生成這些API對應的一系列別名,然后對帖子的文本內容進行匹配識別,最終得到對應的API。通過對開發者常用的10個API進行具體分析,發現開發人員在討論中最常見的三類情形依次是:程序出錯求助、原理及使用方法詢問、不同API比較差異。而在句式上,開發者傾向于在主語、賓語和狀語中提及API,詞組形式出現的API別名還面臨著與其他同名API混淆的情況。
Robillard等[1]使用正則表達式和啟發式規則將項目文檔中的詞與代碼中相似的術語如類、方法等進行鏈接。Rigby等[2]利用正則表達式識別Stack Overflow討論中涉及的API,然后借助決策樹模型訓練分類器,最終得到與討論內容最相關的API。Subramanian等[3]針對Stack Overflow帖子中的代碼片段,設計了一個簡單地迭代式算法,先通過名字匹配找到候選的API,然后結合參數個數、參數類型、返回值類型等,縮小候選集直到輸出唯一結果。Treude等[4]提出機器學習的方法提取Stack Overflow帖子中的某些特征并進行有監督訓練,然后將得到的相關度排序結果進行比較,最終篩選出與某個API相關的有意義的句子補充顯示在官方文檔中。Jiang等[5]對API官方文檔中的文本進行實體識別、代詞和變量消解、句子識別和句子類型識別,最后使用主題模型和PageRank進行相似度計算,從而給用戶推薦與查詢API相關的文本片段。Gall[6]針對API官方文檔與源代碼可能有差異的問題,利用程序分析從源代碼中獲取抽象語法樹并歸納代碼中包含的約束,再使用自然語言處理技術人工歸納文檔中的規則并自動化的抽取約束,最終對范圍約束、控制約束、類型約束這三類的約束進行文檔中和代碼實現的差異對比,從而幫助用戶正確使用API。APIBot[7]在現有的人工智能助手SiriusQA的基礎上進行修改,將API官方文檔上的結構化信息提取出來,然后歸納可回答的問題類型;再判斷問題語句中的API名字是否包含在文檔中方法篩選候選集,然后使用通用問答模式和領域知識兩種方法計算相似度;最后進行與問題相關的官方文檔內文本推薦。可以發現在項目內以及官方文檔上的API研究較多,而在Stack Overflow上進行的API研究則是集中于API的識別以及與官方文檔內容的匹配,缺少對于開發人員的API討論主題與習慣的研究。
Anderson等[8]將Stack Overflow帖子中的一個問題與它的所有回答作為基本單元,然后隨著社區活動的不斷進行,觀察帖子的最佳回答與一些特征的關系,最后歸納投票與聲譽機制的一系列特點。Vasilescu等[9]認為問答網站能夠幫助開發人員提高知識并加快開發過程,但參與問答過程也可能影響正常工作的效率,所以針對Github內的用戶代碼修改過程以及其在Stack Overflow的發帖情況進行了分析。Movshovitz-Attias等[10]挖掘出Stack Overflow中不同聲譽的用戶的發帖模式,并通過對用戶的交互網絡進行圖分析,發現能對網站做出長期貢獻的開發人員。Ye等[11]使用CRF模型,綜合了字典、詞形、詞語聚類特征,通過有監督訓練得到實體識別模型并將Stack Overflow帖子中的軟件實體分成預定義的5類,從而幫助用戶更好地理解不同概念。可以看到之前的研究偏向Stack Overflow本身的機制及用戶發帖行為上的研究,而本文的研究將在更細的粒度上針對用戶發帖中涉及的API領域進行句式和語義上的歸納分析。
API實體識別整體流程如圖1所示。先解析官方文檔頁面進行API實體集合的收集,然后定義別名生成規則構造API別名庫,最后進行文本匹配識別出Stack Overflow帖子中用戶討論的API。

圖1 API實體識別流程概述
本文依據Java和Android官方文檔頁面的布局樣式,構建包裝器對其頁面結構進行解析,得到當前使用廣泛的Java 8以及Android 26版本中的所有API實體(即包、類、接口、方法、字段)及其全限定名。代表API實體的全限定名示例如下:
(1) java.util代表包含Java中常用工具類的包;
(2) java.lang.Integer代表Java中基本數據類型int的封裝類;
(3) java.lang.Cloneable代表Java中需要實現clone方法的類所需要的接口;
(4) android.widget.TextView.setText(java.lang.CharSequence text)代表Android中文本組件設置文本的方法(方法內的參數類型除基本數據類型外均使用類的全限定名);
(5) java.lang.Integer.MAX_VALUE代表Java中整數數據類型中的最大整數值。
由于開發人員在問答網站中交流問題時,或因為自己的表達習慣,或因為某些API的廣泛使用性以及對其他用戶專業水平的認可,會使用各種各樣的別名來替代API實體的全限定名形式。所以為了分析開發人員對于較為通用的別名的討論情況,本文定義如下形式的規則生成API別名:
(1) API實體的全限定名;
(2) API實體全限定名最后一個點號之后的部分;
(3) 簡單方法名與簡單參數類型;
(4) 簡單方法名與參數名;
(5) 簡單API名按照駝峰式命名拆分后的詞組;
(6) 簡單API名按照下劃線拆分后的詞組。
其中:(3)、(4)僅針對API方法。以方法API android.widget.TextView.setText(java.lang.CharSequence text)為例,除第1類別名外可以生成別名:setText(java.lang.CharSequence text);setText(CharSequence);setText(text);set text。而字段API java.lang.Integer.MAX_VALUE不能生成第5類別名,但是存在第6類別名integer max value。通過對所有API根據相匹配的規則生成別名,最終得到API別名庫。
使用Beautiful Soup對Stack Overflow帖子進行去標簽處理,然后借助NLTK對文本內容進行分詞及大小寫轉換等處理,最后對文本內容與別名庫中別名在同為小寫的形式下進行最大匹配(即別名完全出現在文本的某個詞語中),則代表該用戶在帖子中提及別名對應的API實體。
根據網絡上常用Java和Android API的討論,本文選擇表1中所列的10個API作為研究對象。通過上述提出的API實體識別方法,在包含Java和Android標簽的Stack Overflow帖子內對每個API識別出的100個帖子進行人工分析。為了公平探討不同別名形式的API的使用情況,對于API的不同別名取包含該別名的同等數量帖子進行分析(即API有5個別名,則取含有每個別名的20條帖子)。

表1 常用API介紹

續表1
對于android.app.Activity API,50個包含第1類別名的帖子中,32次別名出現在詞語cast to之后,即用戶出現了類型轉換的程序錯誤;7次別名出現在import之后,作為基礎類進行引用;7次別名出現在assignable to之后,表示用戶遇到了方法參數類型使用錯誤的情況;有個別的別名出現在class之后,表示用戶特指該類;以及有用戶對該API有設計原理上的提問如圖2所示。可以發現,Activity API的第1類別名在句子中以賓語的成分出現較多,偶爾存在主語情況。另外50個第2類別名activity中,別名基本以in activity、an activity、my activity和main activity的形式出現,且在句子中作為狀語,而此時用戶帖子一般討論的內容并非以該API為中心,而是其他出現在此組件中的元素。

圖2 Activity類全限定名形式別名出現情況
對于java.lang.String API,50次第1類別名中,22次作為方法參數出現;5次作為方法返回值在方法名前出現;剩下的情況則作為主語和賓語,搭配不固定。第1類的別名提及主要是關于Java中String類型的特點討論以及作為其他API的組成部分出現較多。第2類別名String,18次出現在convert to詞組中,表現了用戶們對于String與其他類型的轉換比較關心;18次以Java表達式的賦值類型出現;剩余的出現情況則是如String a and b、getText()(returns String) method這樣的非規范的自然語言形式。
java.util.Scanner API的第1類別名21次出現在import之后;與using搭配作為狀語出現了7次;其他大多是以賓語形式出現。從整體語義上來看,將近70%的討論是關于導入該類后編譯的錯誤,其他的多為使用方法上的問題討論。而以scanner為表現形式的別名中,13次是表達式賦值類型;12次是以scanner class整體詞組的形式出現;其他的則非固定地后接method、object等詞。該類別名依然是與異常錯誤相關的討論最多,其次則是對于不同輸入讀取方法的比較優劣,最后是對功能的介紹討論。
java.lang.NullPointerException API由于本身所代表的報錯含義,所以100個帖子中83個是各種情況下程序出錯的求助,且用戶們出現最多的錯誤是數據庫相關錯誤、字符串相關錯誤以及swing控件出現的錯誤,剩下17個則是與其原理相關的繼承使用、捕獲順序等方面的討論。該API的三類別名形式均以賓語的形式出現最多,其中第1類、第2類別名在receive、get等詞語后出現較為常見。
java.io.InputStreamReader API對應的第1類別名中,半數以上的別名出現在in和at之后作為狀語表示報錯情況,剩余的大部分情況是作為import的對象進行導入。經分析發現,絕大多數報錯是與XML格式數據解析相關的,少數是其使用方法的討論。第2類別名常出現在with、using、in之后作為狀語,大部分是與該API使用時的緩沖區機制與編碼格式的討論,少數是與其他流讀取API的比較,很少有程序報錯相關的提問。第5類別名詞組以主語及賓語的形式較多出現,且大多是與網絡通信及多線程讀取相關的API特性討論如圖3所示,極少是程序出錯的提問。

圖3 InputStreamReader類駝峰式分詞別名出現情況
對于點擊事件注冊方法的API,第1類、第2類全限定名別名可能是由于書寫過于繁瑣的原因,分別只有1個和2個帖子以這樣的形式提及該API,且出現在代碼片段中。除去第2類別名括號中的形參l的形式在帖子中較常見,其與第3類別名在句子中的成分大多為句子主語或賓語,且兩者基本都在討論程序出錯。第4類別名由于符合Android語言規范中方法調用的形式,所以全部出現在代碼片段中。第5類別名雖然以set詞語開頭,但是除70%情況是以謂語的形式出現外,還有30%的情況是以詞組整體作為主語和賓語。第4類、第5兩類別名均以使用方法的介紹問答為主。
java.util.Date.getTime() API的33次包含第1類別名的帖子中,21次作為主語,剩下的情況為賓語。其中,賓語出現的帖子基本是位于詞語method后描述調用該方法時程序出錯,主語情況則以解釋功能居多,少數是不同獲取時間戳的方法之間的比較以及對于輸出格式的討論。第2類別名中半數以Date類型對象的方法調用形式出現,其余大多在using、with、from后作為狀語。用途上多為不同API之間的比較,少數為相同條件下多次調用該方法結果不一致的討論。第5類別名get time由于較為符合日常交流的方式,多為謂語動詞詞組,且在語境中表示用戶意圖為主,很少特指該API。
android.view.View.findViewById(int id)第1類別名沒有被用戶提及,但是去除了括號內的形參id后的形式出現十分廣泛,且均會以如圖4所示的Android標準空指針異常的形式作為文本出現。第2類別名以主語和賓語的成分對半出現,大多數是與方法返回值類型的轉換有關的問題,少數是方法原理討論與程序出錯。第3類別名均為程序錯誤問題,絕大多數為實際情況下方法參數不正確的錯誤,少數為該非靜態方法在靜態上下文中調用的錯誤。第4類別名出現極少,觀察發現用戶習慣用實際參數來代替形參id。第5類別名中主語、謂語、賓語、狀語出現的情況較為平均,主要為程序錯誤描述和使用方法詢問。

圖4 findViewById方法特殊別名出現情況
Android文本框文本設置API的第1類別名與findViewById的第1類別名情形比較相似,也是去除形參text的形式出現廣泛,且是以相同句式討論異常錯誤。第2類別名在帖子中也極少出現,考慮到可能是因為CharSequence類型屬于開發者較為熟知的類型,所以無需以全限定名的形式出現在方法中。第3類別名基本以主語形式出現,且20個帖子中18個是與程序出錯相關,其余2個則為與其他重載方法的對比。第4類別名只有1次出現,且作為狀語出現在with后,值得注意的是開發者們傾向于使用無參的方法setText()在文中指代該方法。第5類別名在帖子中常作為謂語,而語義上開發者們更傾向于與文本的其他屬性相關的討論,如文本大小、文本顏色等,而不是使用該API設置文本。
在Java獲取數據庫連接的API中,前兩類別名可能是因為過于冗長,沒用實際使用的樣例。第3類、第4類別名基本作為主語和賓語進行使用介紹。第5類別名大多數屬于謂語成分,且由于略去方法參數,所以大部分的討論是關于同名無參的HTTP API方法的討論,剩余的一部分有如何建立數據庫連接的討論以及連接中包導入路徑錯誤等的問題。
綜合分析Stack Overflow中對于API的用戶討論,如圖5所示,在句子成分上,不符合自然語言表達習慣的文本很多,其中API常作為導入的語句以及賦值表達式;在正常文本中,賓語形式的API出現最多,之后是相差不太大的主語和狀語,最后是謂語。且前四類單詞式別名以主語、賓語、狀語為主,后兩類詞組式別名多為謂語。如圖6所示,從語義上來看,用戶們最常見的討論還是關于程序出錯求助,其次是使用原理及方法探討,最后是同類型API間的比較。值得注意的是,用戶們還常常使用實參或無參的方法來指代有參方法,對于名稱過長或是其中參數類型過長的API,用戶們習慣簡寫形式,所以后續研究者在進行API識別時需要特殊考慮。

圖5 Stack Overflow用戶API討論所屬句子成分分布

圖6 Stack Overflow用戶API討論語義分布
基于本文的研究結果,后續研究者可以針對Stack Overflow中用戶在API討論中句式上的習慣和偏好,有針對性地修改現有的如StanfordNLP、ClausIE等依存語法分析工具,更好地自動化抽取Stack Overflow數據中與API討論相關的主謂賓三元組,從而獲取更多在實際開發中用戶發掘的API特性。
針對不同API討論中涉及的語義主題,編程語言的開發者可以深入挖掘,探索不同API引發不同類型的程序異常的原因,分析用戶對于API的使用需求,以及細化相似API所適用的不同場景,從而優化后續API版本的開發以及更好地編寫官方文檔,最終方便用戶的使用。
實體鏈接是實體識別之后對于API識別的最終步驟,而在越來越多深度學習的技術被應用在該領域的情況下,本文發現的不同API在被用戶討論時的語法和語義的特點,將有助于探索那些特征更有利于構建深度學習的模型,從而更好地解決廣泛存在的同名API鏈接困難以及API與概念難區分的問題。
本文針對軟件開發問答網站中對于開發者關于API討論的研究較少的問題,收集Java與Android API集合,定義別名規則構造別名庫,通過文本匹配進行文本內API的實體識別。分析了10個常用的Java和Android API 在Stack Overflow中的討論主題,得到了一系列對于后續研究人員有幫助的Stack Overflow中用戶關于API的語法和語義偏好。