2018年軟工第二次結對做業

軟工結對做業之詞頻統計plusplus


引子

本次做業
隊友 董鈞昊
Github傳送門html

1. 分工明細

  • 031602509 董鈞昊:Java爬蟲的設計實現,附加題的設計實現
  • 031602523 劉宏巖:升級優化WordCount的功能,命令函參數的使用

2. PSP表格


psp2.1 personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 10 10
Estimate 估計這個任務須要多少時間 10 10
Development 原型開發 600 900
Analysis 需求分析(包括學習新技術) 60 120
Design Spec 生成設計文檔 60 60
Design Review 設計複審 20 150
Coding Standrd 代碼規範(爲目前的開發制定合適的規範) 15 15
Design 具體設計 60 180
Coding 具體編輯 180 180
Code Review 代碼複審 120 120
Test 測試(自我測試,修改代碼,提交修改) 30 30
Reporting 報告 210 300
Test Repor 測試報告 30 45
Size Measurement 計算工做量 10 15
Postmortem&Process Improvement Plan 過後總結,並提出過程改進計劃 20 15
- 合計 1435 2150

3. 設計思路及實現流程


  • 仔細閱讀了做業文檔後,總結了本次做業的兩點要求:
    • 爬取數據,並按照格式生成result.txt
    • 升級第一次做業的WordCount,知足各類新增需求
  • 有些功能在上次我的做業中已經說明,因此本次做業就不贅述了,要心疼一下咱們兩位可愛的助教小姐姐~

3.1 Java爬蟲

  • 最初爲了方便起見,用python實現了一遍爬蟲,可是考慮到測試問題,最終仍是換用JAVA實現了一遍爬蟲。
  • 以下用流程圖形式說明個人具體思路
  • 經過分析下圖所示的網頁源代碼,咱們能夠容易看出,每一份論文的標題URL都被保存在ptitle這個標籤中,咱們只需經過循環匹配每個標籤上述標籤就能夠根據得到每一篇論文的標題Title——標籤內容

  • 摘要Abstract的獲取則能夠經過上圖所示中的URL,訪問對應子頁面。分析子頁面的源代碼,咱們能夠看出,摘要被保存在了div id="abstract" 這個標籤中,只需匹配標籤頭尾便可獲取標籤內容——摘要內容

  • 簡單說一下本次爬蟲的幾個注意事項:
    • 首先是爬取摘要時,我下意識認爲標籤尾部只需匹配到 /這個字符即匹配到標籤尾,但實際上摘要部分是有可能出現相似於/、div這兩種字符串的,因此仍需全文匹配標籤尾部。
    • 固然提到後向匹配的問題,C++11非boost庫不支持後向零寬斷言 (具體差別詳見上一篇博客),而JAVA這邊的庫支持大多數正則表達式匹配語言(少許須要外部庫支持);本次也能夠利用後向零寬斷言來實現匹配標籤尾部的功能。
    • !!!查閱了部分資料後發現,在JDK1.3及以前的JDK版本中並無包含正則表達式的類,若是要在Java中使用正則表達式必須使用第三方提供的正則表達式庫。從JDK1.4開始提供了支持正則表達式API,它們位於java.util.regex包中。因此使用JAVA實現爬蟲最好採用JDK1.4版本以上

3.2 代碼組織與內部實現設計(類圖)

  • 本次迭代的新版本代碼僅在基礎上修改完善了部分函數。
  • 並在我的做業基礎上優化了原型代碼,去除舊代碼冗餘部分
  • 迭代更新大體內容以下所示
模塊 備註
work_2.h 包含頭文件、數據結構以及用到函數的聲明
Count_chrs.cpp 統計字符數模塊(也包含行數的統計)
Count_words.cpp 統計單詞、詞組數模塊(結果計入hashmap)
Rank_words.cpp 詞頻、詞組字典序導出模塊,用於實現hashmap
WordCount.cpp 主函數

3.3 命令行解析

  • 命令行解析主要經過循環參數斷定實現,使用一個循環體來斷定傳入參數首字符與次字符;同時加入非法檢測模塊,對不合法參數進行檢測並提供錯誤提示。
  • 值得一提的是,在Unix網絡編程下,C++有一個getopt()函數能夠快速有效地解析命令行參數,具體應用
for (v = 1; v < argc; v++)
    {
        if (argv[v][0] == '-' && strlen(argv[v]) == 2)
        {
            if (argv[v][1] == 'i')          //輸入參數斷定
            {
                if (v + 1 < argc)
                {
                    strcpy_s(fnew.file_name, argv[v + 1]);
                    fnew.i_flag = 1;
                    v = v + 1;
                }
            }
            else if (argv[v][1] == 'o')     //輸出參數斷定
            {
                if (v + 1 < argc)
                {
                    strcpy_s(fnew.out_file_name, argv[v + 1]);
                    fnew.o_flag = 1;
                    v = v + 1;
                }
            }
            else if (argv[v][1] == 'w')
            {
                if (v + 1 < argc)
                {
                    fnew.w_flag = 1;
                    if (argv[v + 1][0] == '0')  //權重參數斷定
                        fnew.weight = 0;
                    else if (argv[v + 1][0] == '1')
                        fnew.weight = 1;
                    else
                        cout << "error for weight!" << endl;
                    v = v + 1;
                }
            }
            else if (argv[v][1] == 'm')
            {
                if (v + 1 < argc)
                {
                    fnew.m_flag = 1;
                    fnew.count_m = atoi(argv[v + 1]);   //詞組參數斷定
                    if (fnew.count_m == 0)
                    {
                        cout << "error for m!" << endl;
                        return 0;
                    }
                    v = v + 1;
                }

            }
            else if (argv[v][1] == 'n')
            {
                if (v + 1 < argc)
                {
                    fnew.n_flag = 1;
                    fnew.count_n = atoi(argv[v + 1]);
                    if (fnew.count_n == 0)
                    {
                        cout << "error for n!" << endl; //排名參數斷定
                        return 0;
                    }
                    v = v + 1;
                }

            }
        }
    }
    if (fnew.n_flag == 0)
        fnew.count_n = 10;
    if (fnew.i_flag == 0 || fnew.o_flag == 0 || fnew.w_flag == 0)   //三參數異常斷定
    {
        cout << "parameters error!" << endl;
        return 0;
    }
  • 本模塊也可簡化以下述流程圖所示

3.4 w—權重統計

  • 標題中的單詞(詞組)在統計時算出現十次,摘要中的單詞(詞組)在統計時算出現一次。java

  • 實現這個功能關鍵就是區分標題和摘要,咱們的思路是:
  • 在提取單詞時,經過單詞Title:Abstract:來檢測,並設置一個開關istitle。當檢測到Title:時,置istitle爲1,表示在此狀態下提取出的單詞(詞組)都屬於標題部分。不然置爲0,表示在此狀態下提取出的單詞(詞組)都屬於摘要部分。這樣就把單詞(詞組)用標題和摘要區分開啦!
  • 固然光有這些還不夠,還要根據w後的參數來判斷是否須要權重功能。敲重點!!! istitlew後的參數同時爲1時,才能把標題中的單詞(詞組)按照10計數。 不然無論是標題仍是摘要中的單詞(詞組),都只能按照1計數。
  • 由於咱們採用了hashmap的方法統計頻,因此只要在插入哈希表的函數上稍做改動便可,即增長一個開關flag,當須要計數10次時置flag=1,插入結點的times值置爲10,不然flag=0插入結點的times值置爲1。node

  • 本模塊的流程圖:python

  • 貼上關鍵代碼,關鍵處用註釋解釋:
//istitle開關部分

if (temp_word == "title"&&temp_line[k] == ':'&&k <= temp_line.size())
{
    fn.count_chars = fn.count_chars - 7;
    count_words--;      //title不算有效單詞
    istitle = 1;        //表示目前處於標題狀態
    wordbuf.clear();    //標題和摘要狀態切換時,清空隊列由於詞組不能跨越兩者
    k += 2;             //跳過「: 」
    continue;           //獲取下一個單詞
}
if (temp_word == "abstract"&&temp_line[k] == ':'&&k <= temp_line.size())
{
    fn.count_chars = fn.count_chars - 10;
    count_words--;      //title不算有效單詞
    istitle = 0;        //表示目前處於標題狀態
    wordbuf.clear();    //標題和摘要狀態切換時,清空隊列由於詞組不能跨越兩者
    k += 2;             //跳過「: 」
    continue;           //獲取下一個單詞
}
//插入函數
void hash_insert(Wordnode **l, string word_str,int weight_flag)
{
    int value = hash_index(word_str);                   //計算哈希值
    Wordnode *p;
    //cout << value;
    for (p = l[value]; p != NULL; p = p->next)          //查找節點並插入
    {
        if (word_str == p->word)                        //已有節點存在(重複單詞)
        {
            p->count += 1 + weight_flag * 9;
            return;
        }
    }
    p = new Wordnode;                                   //未有節點存在(新單詞)
    p->count = 1 + weight_flag * 9;
    p->word = word_str;
    p->next = l[value];
    l[value] = p;
}
//帶權重的插入部分
if (istitle == 1 && fn.weight == 1)
{
    hash_insert(l, temp_comb,1);            //插入10次
}
else
{
    hash_insert(l, temp_comb,0);            //插入1次
}

本模塊可單獨分割進行測試,使得本模塊總體耦合度較低,且本模塊內部幾乎無冗餘代碼,也體現了內聚度較高的性質,完全體現了高內聚、低耦合的特色。ios

3.5 m—詞組統計

  • 本參數要求根據m後的參數,統計對應長度的詞組詞頻。
  • 咱們總結了幾個要點:
    • 如何提取對應長度的詞組
    • 準確並完整的提取單詞之間的分隔符
    • 如何避免非法單詞的干擾(數字,the、we等)
    • 避免跨越標題和摘要提取單詞組成詞組
  • 如下是咱們的思路:git

  • 首先定義短單詞、數字開頭的單詞TiltleAbstract爲非法單詞,其他爲合法單詞 。
  • 在提取詞組時,首先就是連續提取m個單詞。咱們採用了隊列的思想。每當提取出一個單詞時連同它後面的全部分隔符組成一個string型變量進入隊列。而後判斷隊列中的合法單詞數目,若是等於m,就建立一個臨時的空的string型變量,用它去鏈接隊列中的前m個合法單詞。而後去掉鏈接後所得字符串後面的全部分隔符,最後插入哈希表,並彈出隊首單詞。這樣咱們就取得了完整的分隔符。
  • 要去除非法單詞的干擾,只需在檢測到非法單詞時清空隊列便可。由於隊列中的單詞數目不足m且不能後後面進來的單詞組合成詞組。
  • 要去除TiltleAbstract的干擾,每當咱們檢測到這兩個單詞時採用continue忽略,並清空隊列,由於隊列中的單詞數目不足m且不能後後面進來的單詞組合成詞組。因此咱們要清空隊列,防止跨越標題和摘要提取單詞組成詞組。github

  • 本部分流程圖:正則表達式

  • 貼出關鍵代碼,關鍵處用註釋解釋:
//詞組統計函數
if (temp_chr <= '9'&&temp_chr >= '0')
{
    k++;                                //數字開頭判斷
    while (k <= temp_line.size() - 1 && ((temp_line[k] <= '9'&&temp_line[k] >= '0') || (temp_line[k] <= 'z'&&temp_line[k] >= 'a')))
    {
        k++;    
    }
    wordbuf.clear();                    //數字開頭,清空隊列
}
else if (temp_chr <= 'z' && temp_chr >= 'a')
{
    temp_word = "";
    temp_word += temp_chr;      //字符補足
    //cout<<temp_word<<endl;
    k += 1;
    if (temp_line.size() - 2 > k)   //判斷餘下位數是否夠裝下一個單詞
    {
        while (k < temp_line.size() && ((temp_line[k] <= 'z' && temp_line[k] >= 'a') || (temp_line[k] <= '9' && temp_line[k] >= '0')))
        {
            temp_word += temp_line[k];
            //cout << temp_word << endl;
            k += 1;
        }
        //wordbuf.clear();
    }
    //檢測到合法單詞
    if (3 < temp_word.size() && (temp_word[0] <= 'z'&&temp_word[0] >= 'a') && (temp_word[1] <= 'z'&&temp_word[1] >= 'a') && (temp_word[2] <= 'z'&&temp_word[2] >= 'a') && (temp_word[3] <= 'z'&&temp_word[3] >= 'a'))
    {
        count_words++;
        if (temp_word == "title"&&temp_line[k] == ':'&&k <= temp_line.size())
        {
            fn.count_chars = fn.count_chars - 7;
            count_words--;      //title不算有效單詞
            istitle = 1;        //表示目前處於標題狀態
            wordbuf.clear();    //標題和摘要狀態切換時,清空隊列由於詞組不能跨越兩者
            k += 2;             //跳過「: 」
            continue;           //獲取下一個單詞
        }
        if (temp_word == "abstract"&&temp_line[k] == ':'&&k <= temp_line.size())
        {
            fn.count_chars = fn.count_chars - 10;
            count_words--;      //title不算有效單詞
            istitle = 0;        //表示目前處於標題狀態
            wordbuf.clear();    //標題和摘要狀態切換時,清空隊列由於詞組不能跨越兩者
            k += 2;             //跳過「: 」
            continue;           //獲取下一個單詞
        }
        if (fflag == 1)                     //只統計詞組
        {
            int thischar = k;
            //鏈接單詞後的分隔符
            while (isdivide(temp_line[thischar]) && thischar < temp_line.size())
            {
                temp_word = temp_word + temp_line[thischar];
                thischar++;
            }
            wordbuf.push_back(temp_word);       //進入隊列
            if (wordbuf.size() == len)          //長度知足要求
            {
                for (int i = 0; i < len; i++)
                {
                    temp_comb += wordbuf[i];
                }
                wordbuf.erase(wordbuf.begin());     //第一個單詞清空
                while (isdivide(temp_comb[temp_comb.length() - 1]))     //去除最後的分隔符
                {
                    temp_comb = temp_comb.substr(0, temp_comb.length() - 1);
                }
                if (istitle == 1 && fn.weight == 1)
                {
                    hash_insert(l, temp_comb,1);
                }
                else
                {
                    hash_insert(l, temp_comb,0);
                }
                temp_comb = "";
            }
        }
        else                //只統計單詞
        {
            if (istitle == 1 && fn.weight == 1)
            {
                hash_insert(l, temp_word,1);
            }
            else
            {
                hash_insert(l, temp_word,0);
                //cout << temp_word << endl;
            }
            temp_word = "";
        }
    }
    else            //碰見短單詞
    {
        wordbuf.clear();    //清空隊列
    }
}

4. 性能分析與改進


  • 使用爬取得到的result.txt,參數爲WordCount.exe -i result.txt -o output.txt -m 3 -n 10 -w 1,性能測試結果以下圖:

  • 能夠看出消耗最大的函數爲C_words()函數,由於該函數執行單詞提取和詞組拼接的操做,佔用的大部分的CPU時間,總時間爲6.072s
  • 對此咱們提出來靜態模塊轉換爲動態模塊,不只是從算法層次上提出的,更是從模塊化層次提出的。
  • 具體展現模塊以下示對比圖體現

  • 在以前咱們經過靜態判斷詞組及其之間的分割符,每次程序運行過程當中均需花費大量的時間,維護該靜態存儲數據結構,且每次都須要遍歷整個數據結構才能實現詞組的篩選匹配
  • 而改進以後,咱們採用動態數據結構的形式,同時將模塊中的靜態化接口模塊均轉化成動態化程序應用模塊,每次僅僅須要遍歷部分數據結構,且採用二分法的遍歷形式,極大程度的減小了再遍歷的開銷,使得代碼可以以近乎單次徹底遍歷的結果
  • 改進後的效果以下圖所示
  • 改進後時間爲5.232s,性能速度提高比爲13%

5.單元測試


  • 設定了10個單元測試用於檢測代碼的準確性,以下表所示:
單元測試的內容 測試的模塊 輸出結果 測試結果
給定一個字符串 字符統計 字符數 經過
給定論文部分摘要A 單詞統計 字符數 經過
給定論文部分摘要B 詞頻統計 排名前5詞組 經過
非法參數 容錯檢測 錯誤提示 經過
輸入文件異常 容錯檢測 錯誤提示 經過
輸出文件異常 容錯檢測 錯誤提示 經過
result.txt 帶權重單詞統計 排名前五單詞 經過
result.txt 長度爲二、不帶權詞組統計 長度爲2,排名前五詞組 經過
result.txt 長度爲三、帶權詞組統計 長度爲3,排名前五詞組 修改代碼後經過
result.txt 長度爲五、不帶權詞組統計 長度爲5,排名前五詞組 修改代碼後經過

單元測試結果以下圖所示:
算法

  • 這裏提供八、9兩次測試的代碼
//第八次單元測試
TEST_METHOD(TestMethod8)
{
    // TODO: 在此輸入測試代碼
    File fnew;                 //控制文件模塊
    Words wnew;                //控制單詞模塊
    Wordnode *log_list[HASH_LENGTH] = { NULL };  //哈希散列指針數組
    vector<string> file_str;

    fnew.n_flag = 1;
    fnew.m_flag = 1;
    fnew.w_flag = 1;
    fnew.count_m = 2;
    fnew.count_n = 5;
    fnew.weight = 0;



    ifstream f;
    f.open("E:/result.txt", ios::in);       //打開文件
    fnew.count_chars = C_chars(f, fnew, wnew, file_str);    //計算字符數(行數)
    fnew.count_words = C_words(f, fnew, wnew, log_list, file_str);  //計算單詞數(插入哈希節點)
    //cout << "chars = " << fnew.count_chars << "," << "lines = " << fnew.count_lines<<","<<"words = "<<fnew.count_words<<endl;
    rank_word(log_list, wnew, fnew);                        //詞頻排名

    Assert::AreEqual(wnew.word_rank[1], string("this paper"));
    Assert::AreEqual(wnew.count_rank[1], 469);

    Assert::AreEqual(wnew.word_rank[2], string("show that"));
    Assert::AreEqual(wnew.count_rank[2], 340);

    Assert::AreEqual(wnew.word_rank[3], string("neural networks"));
    Assert::AreEqual(wnew.count_rank[3], 209);

    Assert::AreEqual(wnew.word_rank[4], string("neural network"));
    Assert::AreEqual(wnew.count_rank[4], 187);

    Assert::AreEqual(wnew.word_rank[5], string("convolutional neural"));
    Assert::AreEqual(wnew.count_rank[5], 186);

}
//第九次單元測試
TEST_METHOD(TestMethod9)
{
    // TODO: 在此輸入測試代碼
    File fnew;                 //控制文件模塊
    Words wnew;                //控制單詞模塊
    Wordnode *log_list[HASH_LENGTH] = { NULL };  //哈希散列指針數組
    vector<string> file_str;

    fnew.n_flag = 1;
    fnew.m_flag = 1;
    fnew.w_flag = 1;
    fnew.count_m = 3;
    fnew.count_n = 5;
    fnew.weight = 1;



    ifstream f;
    f.open("E:/result.txt", ios::in);       //打開文件
    fnew.count_chars = C_chars(f, fnew, wnew, file_str);    //計算字符數(行數)
    fnew.count_words = C_words(f, fnew, wnew, log_list, file_str);  //計算單詞數(插入哈希節點)
    //cout << "chars = " << fnew.count_chars << "," << "lines = " << fnew.count_lines<<","<<"words = "<<fnew.count_words<<endl;
    rank_word(log_list, wnew, fnew);                        //詞頻排名

    Assert::AreEqual(wnew.word_rank[1], string("convolutional neural networks"));
    Assert::AreEqual(wnew.count_rank[1], 196);

    Assert::AreEqual(wnew.word_rank[2], string("generative adversarial networks"));
    Assert::AreEqual(wnew.count_rank[2], 178);

    Assert::AreEqual(wnew.word_rank[3], string("convolutional neural network"));
    Assert::AreEqual(wnew.count_rank[3], 159);

    Assert::AreEqual(wnew.word_rank[4], string("visual question answering"));
    Assert::AreEqual(wnew.count_rank[4], 156);

    Assert::AreEqual(wnew.word_rank[5], string("deep neural networks"));
    Assert::AreEqual(wnew.count_rank[5], 128);

}

6. 代碼覆蓋率測試

  • 使用Run OpenCppCoverage工具測試了本次做業程序的代碼覆蓋率,結果以下圖所示:

  • 能夠看出覆蓋率較低的模塊爲main函數count_char函數,主要緣由以下述所示:
    • main函數中加入了許多異常檢測代碼段,好比文件打開失敗檢測等,致使覆蓋率下降。
    • count_char函數中,爲了方便統計單詞,咱們把文檔事先都轉化爲小寫字母,因爲爬取結果中大寫字母數目不多,因此致使大小寫轉換的函數的使用率不高,代碼覆蓋率下降。

7.附加題設計

  • 咱們的附加題實現的功能主要是圍繞CVPR的論文處理進行,咱們可經過批量爬取論文PDF保存至本地讀取論文PDF轉換文字論文關鍵詞、高頻詞檢索並繪製詞雲
  • 爲此咱們經過Tkinter模塊將上述功能整合在一塊兒。
  • 原先是想經過Web繪製較爲好看的界面的,可是礙於時間限制,最終採用簡潔的python模塊進行界面UI設計實現。
  • 在Windows界面下的效果以下圖所示:

  • !!!雖然說咱們結對的倆人是鋼鐵直男,可是Tkinter的美工界面的也太粗糙了吧,咱們認爲這也與Tkinter在Windows下的底層實現方式以及移植問題相關。
  • 通過查閱文檔後,在Tkinter的首頁上,咱們獲得了答案——Tkinter在Windows上的界面是極其很差看的,因此咱們改用Ubuntu實現咱們的界面。仍不死心
    編程

  • 在Ubuntu系統上配置好了環境後,再次運行程序的結果以下圖所示:

  • 本質上還只是將邊框切換成了Ubuntu風格的界面,本質上內容非常十分簡陋的。既然如此,不妨來看看咱們實現的功能吧。完全死心
  • PDF批量下載模塊

經過爬蟲批量獲取論文PDF的URL,同時完成解析功能,而且以text的形式將其保存在本地對應文件夾中,方便後續讀取PDF功能的實現。

  • PDF轉換文字功能


經過將PDF轉換成文字功能,從圖片擴展到文字,雖然說尚不可及咱們的初衷——OCR識別論文,可是從結果層面考量已經足夠實現這項功能了。
同時這項功能並不僅是爲了後續詞雲,咱們也能夠就此依據Google基於python提供的翻譯庫,實現論文的翻譯,減輕了大多數用戶的閱讀負擔
或許你常會遇到論文中沒法理解的專業術語,而這些術語大可能是簡寫,這時咱們該怎麼辦呢?對此咱們可基於此結合情感分析的手段實現文本處理,大體結果圖以下圖所示:

本次計劃實現的情感分析模塊也會在團隊做業中更加完善化的實現。

  • 基於背景圖片的詞雲生成
    效果對比圖以下圖所示:

咱們可根據用戶所需指定的背景圖片,生成更加有趣、好看的詞雲圖,而不是僅僅基於普通的方框模板的詞雲圖;該項功能給用戶提供了高自由度的自制定模板,從基於官方固定模板用戶充足發揮想象力

私覺得這類能提供給用戶的高自由度功能在當今已是少之又少,市場上更多的則是限制了用戶的思惟方式,而本「產品」反其道行之,盡咱們的最大程度給用戶帶來良好體驗。

8.Github的代碼簽入記錄

  • 匆忙間只迭代了一個版本,但該版本是通過屢次測試、修繕完成的
  • 同時提交了 「爬蟲」腳本及主程序

9.遇到的代碼模塊異常或結對困難及解決方法

  • 本次遇到的困難及各自的應對方式大體以下表中所列。
異常及困難描述 嘗試解決 是否解決 有何收穫
原型代碼的選擇 嘗試測試了雙方程序速度,並比對易讀性 分析了下述多點如程序運行效率、測試結果準確性在內的諸多考量因素,同時考量了各自編碼的優點及可行性來完成分工模塊
爬蟲設計問題 原計劃採用C++實現本次爬蟲,但屢次嘗試均受阻於代碼量極大且功能複雜的爬蟲模塊 解決問題不必定要循序漸進地照標準模型進行,能夠在既有模型的概念基礎上,添加自身的思考與體會,打造出更適合團隊實際應用的模型應用到本次問題中,如果擔憂應用的週期性及實用性,則能夠選擇其餘模塊開發。
鋼鐵直男的審美 嘗試邀請美工「大觸」,學習「生活與美」等相似界面UI教程 直男毀一輩子,美工「大觸」都不屑跟咱們合做,可是這一次咱們的美工完成「從0到1」的改進,從流程圖的繪製到改進對比圖均體現出了咱們審美的極大提高。

10. 學習進度條


第N周 新增代碼(行) 累計代碼(行) 本週學習耗時(小時) 累計學習耗時(小時) 重要成長
1 300 300 18 18 原型設計,爬蟲關於python的urllib庫及request庫學習
2 0 300 8 26 鋼鐵直男們的審美進步「一點點」
3 500 800 12 38 Java爬蟲、Tkinter界面

11.評價隊友

  • 031602509 董鈞昊:

合做了N屢次的好基友,並且每次合做都能讓我獲益匪淺。此次的代碼正是以他第一次我的做業的代碼爲基礎,良好的封裝和功能劃分,讓我很快的就看懂了他的思路,幾乎只用了一天晚上就完成了附加功能的編碼和debug過程,這就是我要向他學習的!
此次結對讓最我吃驚的就是,因爲咱們兩我的十一長假都在忙各自的事情,致使此次做業咱們只用了兩天時間來完成,最後能夠在DDL以前實現全部的功能,要歸功於咱們的高執行力和高效率,很是享受這樣的結對過程,但願之後還有機會能夠再次合做!

12.總結

  • 結對做業從原型到實現,隨然時間跨度不長,但還是一份值得細品其內涵的一份做業。
  • 大多數人偏好 「單打獨鬥」,只願一我的完成整個功能的實現,而忽視了這種作法存在的致命缺陷——主觀意願過強;而結對做業不只提供了一個能從客觀角度幫助你分析的隊友,還提供了一種結對編程形式的模板——其中一人主攻編碼模塊,另外一人則更客觀提出編碼意見促進編碼實現。也算是一種不可多得的編程體驗吧。
  • 最後我仍是想不單僅爲我本身,更爲我優秀的結對隊友鼓下掌吧!

13.附件

14. 更新(10.17)

  • 感謝柯老師幫咱們找出了一個 「bug」
  • 現已修正爲經過採集的CVPR論文繪製出的詞雲,效果以下所示:

  • 選自Misra_Learning_by_Asking_CVPR_2018_paper
相關文章
相關標籤/搜索