在文章開頭給出結對同窗的博客連接、本做業博客的連接、你所Fork的同名倉庫的Github項目地址html
本做業博客連接
github pair c
031602136魏璐煒博客
031602139徐明盛博客java
給出具體分工python
徐明盛:代碼修改,消除警告,性能分析改進,爬蟲
魏璐煒:單元測試,編寫樣例
本次做業沿用了徐明盛同窗的代碼,所以任務分配主要出於效率的考量:徐明盛同窗熟悉本身的代碼;魏璐煒同窗只須要清楚函數接口即可以編寫單元測試。
此外的爬蟲和附加題沒有硬性要求,按時間自行分配。各自都有進行嘗試。git
給出PSP表格github
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | ||
· Estimate | · 估計這個任務須要多少時間 | 20 | 20 |
Development | 開發 | ||
· Analysis | · 需求分析 (包括學習新技術) | 600 | 480 |
· Design Spec | · 生成設計文檔 | 30 | 120 |
· Design Review | · 設計複審 | 20 | 30 |
· Coding Standard | · 代碼規範 (爲目前的開發制定合適的規範) | 0 | 0 |
· Design | · 具體設計 | 30 | 60 |
· Coding | · 具體編碼 | 300 | 180 |
· Code Review | · 代碼複審 | 120 | 480 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 180 | 120 |
Reporting | 報告 | ||
· Test Repor | · 測試報告 | 60 | 30 |
· Size Measurement | · 計算工做量 | 20 | 20 |
· Postmortem & Process Improvement Plan | · 過後總結, 並提出過程改進計劃 | 300 | 300 |
合計 | 1680 | 1840 |
解題思路描述與設計實現說明算法
1.爬蟲使用
用python編寫了代碼。有豐富的函數方便使用,主要流程以下:
須要爬取的信息中,Title、PDF連接和做者是能夠在起始頁面所有獲取的;可是Abstract須要單獨進入詳情頁面獲取(其實做者也是在詳情頁面獲取的,由於得到的字符串不須要再額外加工)。
所以有了上面流程圖中的循環部分。迭代集合是在初始頁面獲取的論文詳情列表;每次迭代都是重複requests.get( ), etree.html( )以及xpath( ),而後將獲取的數據加入到總的數據列表。數組
2.代碼組織與內部實現設計
基本結構與我的做業相比基本不變。額外增長了一些參數以及它們的初始化方法。安全
3.說明算法的關鍵與關鍵實現部分流程圖
新增要求大部分只須要在原代碼中增長一些自定義的參數,這些要求包括指定輸入輸出文件、權重、TopN,不介紹。
最大的改動是詞組要求,且這個詞組要求特別在:
(1)不合法單詞隔斷詞組;
(2)分隔符一塊兒輸出。
我認爲,詞組的累計性(一個單詞能夠在多個詞組中)不適合維持一個當前詞組,每次增減單詞。
我想到,能夠維持一個長度爲m的隊列,隊列中的單詞是詞組的潛在成分。考慮到須要歸入分隔符,所以不直接把單詞放在隊列裏;又注意到詞組老是以單詞開頭以單詞結尾,就能夠在隊列中存放單詞首字母在line(文件中的一行)中的位置,那麼單詞的生成方式是:
line.substr(queue.first(),詞組長度)
其中詞組長度由當前位置i-queue.first() 獲得。ide
這個隊列的重要行爲是push和pop和清空:
push:當發現一個新單詞首字母時,首字母位置push
pop:每輸出一個詞組,長度爲m的套環往前移,pop清空:不合法單詞阻斷詞組,所以清空時機即發現不合法單詞時:函數
附加題設計與展現
在要求爬取的基礎上爬取了做者和PDF連接。
1.實現思路
只須要在原來的爬蟲代碼上增長几個xpath獲取位置就行了。
2.實現成果展現
關鍵代碼解釋
上文中算法提到的隊列及其操做時機是我認爲最重要的部分。尤爲注意以數字開頭的字母數字串也會阻隔單詞,因此要清空單詞隊列。
以及下文消除警告的部分提到的,好比類型轉換;雖然大部分時候沒有影響,可是注重細節的人寫的代碼也給人帶來安全感。
重要的是思想!代碼很簡單,就不羅列了。
性能分析與改進
1.描述你改進的思路
關於性能改進,我擬出的標題是————vector of pair VS. map
這個問題仍是挺有趣的!在上一次的我的項目中,有提到————
我將單詞及其頻率存儲在map中。這是一讀題就作出的決定,由於考慮到map能夠直接經過key值訪問索引,方便我對詞頻計數;然而在後期發現,在對單詞詞頻自增前,須要查詢其存在,這就必需要搜索。既然都搜索了,想必也找到了其索引,所以「經過key值訪問value」的操做顯得沒必要要了。而且,題目要求先對詞頻排序再對key值排序,而map不便於實現。爲此我將map拷貝到vector<pair<string,int> >中。既然老是要使用到vector,而map有沒有體現其優點,不如一開始就使用vector。我這樣使用map,浪費了空間,拷貝到vector浪費了時間。
在上文中已經提到的本例中使用map的缺點以外,由性能分析我還注意到另一點:map之insert————排序
意思就是map在你插入之時,會主動由鍵值排序。然而這種排序不是咱們想要的,咱們要的是頻率優先,因此咱們以後還得本身排序。這就致使了時間的浪費————你map排序白排啦。
看上去一切都在爲款款而來的vector<pair<string,int> > 作鋪墊!
然而在引入容器時,會遇到新的問題:vector之查詢————find_if
vector的find對於pair彷佛是沒有支持,咱們須要更明確的find_if來查找。一番查找後寫好了find_if須要的函數對象,下面是使用對比圖(採用的爬取結果做爲樣例):
是否是!很驚人!地!慢!(注意藍色部分 find_if )
猜想是這種pair容器以及find_if使用的仿函數彷佛是太複雜了,以致於查詢時帶來了更更更更多的時間消耗。
因此map與vector of pair之爭目前看來是前者勝利了,map更迂迴的使用方法雖然帶來了一些額外消耗,但卻具有更方便以及快速的查詢功能(從這一點看來,map的自動排序並無白白浪費),成爲單詞表的優秀人選~
結論:實驗說話,不能夠貌取人。
此外,還作了一些其它努力:
(1)遍歷次數:爲了函數劃分,咱們遍歷了兩遍文件。但如果處於效率考量,遍歷一遍就能夠獲得全部結果了。因而產生了countAll函數,遍歷一遍文件,生成全部結果。兩種調用方法供挑選。
(2)tempWord:原先要維持一個string單詞以存入字典。如今只需記錄單詞長度,改爲int,會比操做字符串簡單。
2.展現性能分析圖和程序中消耗最大的函數
splitPerLine()用於按行分詞組。這確實是程序運行的大頭,以前關於map與vector的討論也是出自這裏。
單元測試
(1)運行請在x86環境下運行
(2)單元測試說明
測定內容 | 測試函數 | 最終結果 |
---|---|---|
不存在的文件名(或者錯誤的文件名) | countall() | 經過 |
測定字符數是否正確 | countCharLine() | 經過 |
測定行數是否正確 | countCharLine() | 經過 |
測定單詞數是否正確 | countWord() | 經過 |
統計單詞的頻率功能(含權重\不含權重)且只輸出一個單詞 | frequency() | 經過 |
統計單詞的頻率功能(含權重\不含權重)且只輸出多個單詞 | frequency() | 經過 |
增長特殊用例測試(即詞組中是否存在不合法的單詞) | frequency() | 經過 |
更多詞組形式(不合法+分隔符) | frequency() | 經過 |
使用cvpr爬取的結果測試 | 全部函數 | 經過 |
(3)函數說明
TEST_METHOD(testcountCharLine) { int argc = 11; char **argv; counter *myCounter = new counter; argv = new char *[20]; for (int i = 0; i < 20; i++) { argv[i] = new char[20]; } strcpy(argv[0], "WordCount.exe"); strcpy(argv[1], "-i"); strcpy(argv[2], "input.txt"); strcpy(argv[3], "-m"); strcpy(argv[4], "3"); strcpy(argv[5], "-n"); strcpy(argv[6], "3"); strcpy(argv[7], "-w"); strcpy(argv[8], "1"); strcpy(argv[9], "-o"); strcpy(argv[10], "output.txt"); myCounter->initParameter(argc, argv); pair<int, int> lineresult = myCounter->countCharLine(); Assert::AreEqual(lineresult.first, 74); Assert::AreEqual(lineresult.second, 2); }
TEST_METHOD(testcountWord) { int argc = 11; char **argv; counter *myCounter = new counter; argv = new char *[20]; for (int i = 0; i < 20; i++) { argv[i] = new char[20]; } myCounter->initParameter(argc, argv); int wordresult = myCounter->countWord(); Assert::AreEqual(wordresult, 9); }
TEST_METHOD(testprint) { myCounter->initParameter(argc, argv); myCounter->countAll(); myCounter->frequency(); myCounter->print(); FILE *fp = NULL; fopen_s(&fp, "output.txt", "r"); string fileBuf; char c = 0; while ( (c = fgetc(fp)) != EOF) { fileBuf += c; } string stdFile = "characters: 74\nwords: 9\nlines: 2\n<monday tuesday wednesday>: 11\n<tuesday wednesday thursday>: 11\n<wednesday thursday frida>: 1\n"; Assert::AreEqual(stdFile, fileBuf); fclose(fp); }
(4)運行結果
上述的單元測試代碼是中測試助教給的Wordcount樣例對於這個程序是否運行正確,咱們考慮到的文本狀況爲:
(1)空文本(或者輸入一個不存在的文本)
(2)單詞的各類狀況
· 合法詞組之間穿插着多個分隔符
(例如hhhh qq@aaaa*qad hello等)
· 單詞前面有數字的狀況
(例如123qazd,qqqw852的單詞統計以及詞頻考慮狀況)
· 單詞中夾帶的不合法單詞的狀況
(例如:hhhh in hhhhh)
(3)單詞頻率(主要是對-m模塊的測試)
單元測試時的模塊異常
· 單元測試時須要模擬採用命令行傳參,經過二級指針解決char命令行傳參的問題
· 單元測試中沒有加入環境形成的問題
貼出Github的代碼簽入記錄
遇到的代碼模塊異常或結對困難及解決方法
將消除警告寫在這裏。
1.問題描述
warning LNK4042:對象被屢次指定;已忽略多餘的指定
檢索結果的大意是同名文件生成了同名的.obj文件。試了不少辦法都不ok,最終這個解決了個人問題:
warning C4018:「<」:有符號/無符號不匹配map,vector等使用size方法時返回的是無符號數,我用來與之比較的是整型數,故警告。
解決方法是:1.定義無符號數i與size比較;2.強制轉換。這個問題大多數時候是溫和的,可是在一個時候會出現問題:i<v.size()-1。當無符號數0(容器大小爲零)時,減1會變成一個大數,原本結果爲false如今變成true了,你說問題大不大嘞。
warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
警告緣由是strcpy對於複製時超出內存的行爲沒有定義,因此推薦更安全的strcpy_s。解決過程當中個人思考是:strcpy_s函數是vs本身的函數吧,兼容會不會很差。因而考慮使用strncpy。可是發現strncpy同時也存在危險:可能存在數組沒有以'/0'結尾的問題。因此我仍是使用了strpy_s。
warning C26495: Variable 'counter::inFilename' is uninitialized. Always initialize a member variable (type.6).
警告說輸入文件名指針沒有被初始化。要消除這個警告,能夠在構造函數中:
1.給指針分配空間,並賦默認值「input.txt」;
2.給指針賦值NULL。
這個問題我解決了好久。原由是我很是強迫症地想要動態分配空間。我很是不情願使用方法1,由於我要在以後由用戶輸入的文件名動態分配空間,而使用方法1致使以後須要從新分配空間。麻煩!
可是採用方法2並由輸入動態分配空間會致使下面這個警告:
warning C6387: 「inFilename」多是「0」: 這不符合函數「strcpy_s」的規範。// strcpy_s(inFilename, (strlen(argv[i]) + 2), argv[i]);
有可能在參數中沒有找到輸入文件名,因此inFilename動態分配內存後可能仍爲NULL。消除警告的方法是在strcpy_s前加上inFilename是否爲空指針的判斷。
warning C26444: Avoid unnamed objects with custom construction and destruction (es.84). // myCounter->frequency();
說明:
The warning can flag code that is not compiler generated and that invokes a function that returns an object of a RAII type.
This warning helps to detect ignored call results in addition to wrong declarations.
它會致使:
inefficient code that allocates and immediately throws away resources or code that unintentionally ignores non-primitive data.
報警處的的frequency()函數將會排序單詞表,並返回一個pair容器。可是在主函數中,這個返回值無人接收,成爲local variable with no name,分配得資源又當即拋棄了。
解決辦法是在主函數定義一個變量接收,或者修改函數不返回容器。前者太不必了,故考慮後者:當初我設定這個返回值是爲了方便單元檢測或者往後有別的需求能夠對它直接操做,
但仔細考慮以爲:
1.單元檢測能夠從輸出文件讀取結果,就不直接對字典檢查了;
2.往後新的需求應該增長函數處理,而不是在主函數處理。
這個警告處處都找不到中文資料。因此後人遇到這個問題時,會不會翻到這篇文章呢? Hello from 2018!
2.作過哪些嘗試
固然是搜索因特網。對於找不到中文資料的部分,必應有給相關英文結果,可是不少名詞都沒法在中文語境裏找到對應項。
3.是否解決
√
4.有何收穫
學習英語很重要。代碼質量分析很不錯,會給出編譯不給出的警告。作一個注重規範的人。
評價你的隊友
魏璐煒評價徐明盛:
值得學習的地方:
徐明盛評價魏璐煒:
值得學習的地方:
-認真勤奮,熬了挺多夜的;還在晚上從教學區接我回宿舍討論,感動中國。
值得改進的地方:
-自學能力,獨立解決問題的能力。
學習進度條
第N周 | 新增代碼 | 累計代碼 | 本週學習耗時 | 累計學習耗時 | 重要成長 |
---|---|---|---|---|---|
1 | 200 | 200 | 5 | 5 | 學習python |
2 | 400 | 600 | 6 | 11 | 學習用python爬取信息 |
3 | 300 | 900 | 6 | 17 | 學習java |
4 |