這個作業屬於哪個課程 | 2021春軟件工程實踐|W班(福州大學) |
這個作業的要求在哪里 | 寒假作業2/2 |
這個作業的目標 | 閱讀構建之法,完成wordCount程序,撰寫博客 |
其他參考文獻 | 博客園、csdn |
GitHub項目地址 | https://github.com/LchCangHai/wordcount1 |
任務一:再次閱讀《構建之法》,提出疑問
讀《構建之法》提出的問題
1.單元測試 中提出的問題
出處:2.1單元測試部分。說其功能是:讓自己負責的模塊功能定義盡量明確,模塊內部的改變不會影響其他模塊,而且模塊的質量能得到穩定的、量化的保證。 (以及后文的詳細介紹,這里不全部引用)
我理解的是測試可以從單元測試到回歸測試再到功能測試,變得主要是范圍,都還是以單元測試為基礎。
我想問的是:單元測試是主要用來測試代碼功能是否正確還是主要用來記錄相應模塊功能並在今后的更新中保持穩定的。如果是用來測試代碼功能的話,那要如何確定測試內容,如何保證測試是正確的?書中說單元測試應該由本人完成,但有時候開發人員自身意識不到的問題再測試時也是意識不到的,是否需要多人協助檢查或者提前商定標准?如果是用來記錄模塊功能並在更新中保持穩定的,那么在大型的軟件中,每個模塊甚至每個模塊的不同版本都可能是不同人寫的,如何保證測試代碼、方式的統一以及解決更新功能修改代碼后單元測試代碼的不匹配問題?
單元測試代碼是必要的嗎?還是說有選擇性的,只需要在關鍵的,預料到可能修改的模塊上進行測試?
2.軟件工程技能的判定中的問題
出處:3.3 巴克斯頓說技能的反面是“Problem Solving”—“解決問題”;
問題:他的意思是:當需要花時間對問題進行解決時,就不算是精通技能。要到將低層次問題的解決熟練到納入本能了才算是精通。但有的時候會感覺基礎技能(也就是低級技能)都掌握的很熟練了,會在看到問題的第一時間想到要怎么解決,但僅僅只是知道解決方法,可能具體細化要某個API,就是會知道這個API的存在,但使用時總得去查詢一番(是較少用到或者比較復雜)。這樣的情況,算不算精通?有多個老師曾表達過過類似的意思:現在軟件編寫的語言,種類很多很多,而且編譯器也可以完成很多事情,有一些API等知識不必要記得清清楚楚,只需要知道有,了解下,需要時隨時可以查。這樣的情況我也是可以快速的知道要怎么解決,但只是其中的需要背的部分需要查一下,也算是精通吧。
3.goto的使用問題
出處:4.3.2:函數最好有單一的出口,為了達到這一目的,可以使用goto。只要有助於程序邏 輯的清晰體現,什么方法都可以使用,包括goto
goto一直以來被認為是“有害的”,Dijkstra也曾寫過《go to statement considered harmful》。它也有好處:比如避免函數中出現多個return(就是文中說的可以有單一出口),直接跳出多層循環等。
但第4節中主要寫代碼規范,goto在我看來用起來確實方便但確實看起來很不友好。我覺得他雖然有好處但也盡量不要去使用。
4.團隊集體分工
出處:5.2軟件團隊模式
軟件團隊是要有各種分工,各司其職。但我們在學校學習的話。情況又會很不一樣。又會在有一個學生的身份。若是有人想盡可能的了解,多學習每個職能要做的事的話,會不會貪多嚼不爛?有些人基礎較差,成為整個團隊進度短板要如何處理?一個團隊個分工人數不均衡或者出現偏差(比如產品3個,但沒有美術)這要如何解決?
有沒有適用於校園的,給自發組建團隊或被動組建的團隊的一個開發模式?團隊成員刻意放縱無所作為,找各種理由推脫不肯配合團隊怎么辦?還要考慮各方面因素制約。如何真正地約束團隊成員?
還有就是:團隊模式和團隊開發模式有什么區別?
5.創新的問題
看完16章的思考
創新,很多時候只是靈感乍現,如何讓想法落地,讓想法成為真的創新實踐?就算有想法有決心,路在哪里,怎么走?
冷知識
在程序中bug一詞用於技術錯誤。這一術語最初由愛迪生在1878年提出的,但當時並沒有流行起來。在這的幾年之后,美國上將Grace Hopper在她的日志本中,寫下了她在Mark II電腦上發現的一項bug。不過實際上,她說的真的是“蟲子”問題,因為一只蛾子被困在電腦的繼電器中,導致電腦的操作無法正常運行。如圖片所見,她寫道“這是我在電腦上發現的第一個bug”。
任務二:wordCount開發
詞頻統計程序:github地址
1. PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | ||
• Estimate | • 估計這個任務需要多少時間 | 770 | 910 |
Development | 開發 | ||
• Analysis | • 需求分析 (包括學習新技術) | 60 | 30 |
• Design Spec | • 生成設計文檔 | 50 | 60 |
• Design Review | • 設計復審 | 30 | 60 |
• Coding Standard | • 代碼規范 (為目前的開發制定合適的規范) | 30 | 30 |
• Design | • 具體設計 | 80 | 100 |
• Coding | • 具體編碼 | 180 | 200 |
• Code Review | • 代碼復審 | 100 | 200 |
• Test | • 測試(自我測試,修改代碼,提交修改) | 50 | 100 |
Reporting | 報告 | 45 | 45 |
• Test Repor | • 測試報告 | 45 | 45 |
• Size Measurement | • 計算工作量 | 10 | 10 |
• Postmortem & Process Improvement Plan | • 事后總結, 並提出過程改進計划 | 30 | 30 |
合計 | 770 | 910 |
2. 前置概述
使用c++
語言進行編程,使用visual studio 2019編譯器
共有4個分支:master、unitTest、file、task
- 其中master即為任務要求代碼,直接下載使用即可;
- unitTest是任務代碼的基礎上加上了單元測試項目;
- file是將讀入文件與寫入文件的地址作為輸入。再對文件內容進行統計與結果輸出。
- task是在發現程序使用起來和任務要求不同,從master中分出分支修改后再並回到master中(可以忽略它);
1.git和github使用
git使用可視化工具sourcetree
(牆裂推薦!);
同時也嘗試過大多基本會使用到的git命令行代碼;
首次自己完成 .gitignore
文件;
其中多次格式美化,是在覺得自己代碼已經完善后提交,但接下來又發現新的問題了。
2.代碼規范規定
上傳至github中的codestyle.md
文件中:鏈接
目前自身代碼不是很清晰,會逐步添加進去。
3.解題思路
題目要求分析
1.輸入文件和輸出文件以命令行參數傳入
最開始覺得是以文件讀取和寫入的方式來搞,寫好了復查的時候發現要求是在命令行。命令行指定的文件是代碼的輸入和輸出部分;
現在的格式是:Main.exe <input.txt> output.exe
感觸:將各個功能模塊拆開來真的有用!在我發現寫錯了的時候很好改!!
2.單詞:至少以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。
單詞的判斷方法思路
- 遍歷判斷分隔符:最開始只想到了空格符來判斷(沒看清題目),后來發現問題很多:分隔符范圍較大、單詞間分隔符數量不定、單詞本身還需要再判定。思考后決定用正則表達式對分隔符全部轉化為空格;
- 但!既然想到了正則表達式,我可以直接用正則判斷並獲取單詞啊。於是最終方法得出了
使用的正則表達式:string regexp = "[a-zA-Z]{4}[a-zA-Z0-9]*";
3.輸出頻率最高的10個:1.頻率相同的單詞,優先輸出字典序靠前的單詞;2.輸出的單詞統一為小寫格式
儲存單詞及其個數使用map。
map<string, int>
需要進行排序:先根據單詞(key)排序,再根據單詞出現次數(value)排序;
key排序使用map中自帶的方法,value排序則是將map移到vector上再操作。
單詞存為小寫:在把單詞存入字典前先把起轉化為全小寫單詞;
4.其他數量統計
行數統計:是通過逐行讀取輸入判斷計算並最后累積得出最終結果,所以行數就只需要在逐行讀取時累加;
字符統計:在逐行統計時直接使用
string.length()
再累加;
4.性能改進
就是最開始使用遍歷字符串,逐一判斷是否為單詞組合部分。后面改變方法,使用正則表達式;
5.接口設計與實現
統計的原理是:逐行讀取input文件中的內容。一行一行的判斷然后累加;
代碼組織
原先設計將每個功能模塊的函數都分到不同文件夾中,但發現分開后單元測試會出現鏈接錯誤的error,所以最后還是把所有函數都放在了
head.h
中。
文件head.h
中:
有一個類fun;類中有:
屬性:myMap
(存儲單詞及其數量的字典)、regexp
(判斷單詞的正則表達時);
方法:
int countWord(string str){}
(計算一行(一個字符串中)的單詞數)
void getWord(string str){}
(獲取一行中的單詞及其個數存入map中)
string myToLower(string str)
(單詞轉化為小寫)
void showWord()
(輸出單詞及其數量)
在Main.cpp
文件中創建一個fun類,使用這些函數和屬性。
接口關鍵代碼展示
1.計算一行(一串字符串)中單詞的數量
//計算一行(一個字符串中)的單詞數
int countWord(string str) {
regex words_regex(regexp); // 判斷單詞的正則表達式
auto words_begin = sregex_iterator(
str.begin(),
str.end(),
words_regex);
auto words_end = sregex_iterator();
return distance(words_begin, words_end);
}
2.記錄統計一行中的單詞及其個數
//獲取一行中的單詞及其個數存入map中
void getWord(string str) {
regex words_regex(regexp); // 判斷單詞的正則表達式
auto words_begin = sregex_iterator(
str.begin(),
str.end(),
words_regex);
auto words_end = sregex_iterator();
for (sregex_iterator k = words_begin; k != words_end; ++k) {
smatch match = *k;
string match_str = myToLower(match.str());//獲取單詞
//////////////將單詞放進map統計數量/////////////////
map<string, int>::iterator iter1;
iter1 = mymap.find(match_str);
if (iter1 == mymap.end()) {
pair<string, int> value(match_str, 1);
mymap.insert(value);
}
else {
mymap[match_str]++;
}
}
}
3.其余功能接口
// 轉化為小寫
string myToLower(string str) {
for (int i = 0; i < str.length(); ++i) {
str[i] = tolower(str[i]);
}
return str;
}
//輸出單詞及其數量
void showWord() {
map<string, int>::iterator strmap_iter2 = mymap.begin();
for (;strmap_iter2 != mymap.end();strmap_iter2++)
{
cout << strmap_iter2->first << ' ' << strmap_iter2->second << endl;
}
}
6.單元測試
在
unitTest
分支中
對計算單詞數量(10個例子)和獲取單詞(3個例子)兩個功能進行了單元測試
計算單詞數量中就包含了單詞的判斷;
剛寫好單元測試的時候就判斷出了寫的算法有大問題。於是進行了修改
最后結果:
有對其中“獲取一行中的單詞及其個數存入map中”這個方法進行重新的設計。因為原來的方法沒有返回值。其本質代碼沒變
//測試用函數
int getWordTest(string str, string exam) {
regex words_regex(regexp); // 判斷單詞的正則表達式
map<string, int> mymap;
auto words_begin = sregex_iterator(
str.begin(),
str.end(),
words_regex);
auto words_end = sregex_iterator();
for (sregex_iterator k = words_begin; k != words_end; ++k) {
smatch match = *k;
string match_str = myToLower(match.str());//獲取單詞
//////////////將單詞放進map統計數量/////////////////
map<string, int>::iterator iter1;
iter1 = mymap.find(match_str);
if (iter1 == mymap.end()) {
pair<string, int> value(match_str, 1);
mymap.insert(value);
}
else {
mymap[match_str]++;
}
}
return mymap[exam];
}
7.心路歷程與收獲
-
一定要先仔細的梳理一遍題目要求!單設計代碼的時候如果有發現什么規則。規范第一步先去看看題目中有沒有,不要自己瞎琢磨。不然寫好后要不停改;
-
單元測試真的挺香的,我原先覺得寫這個就是浪費時間。現在覺得,即使它沒檢查出不對,但在寫單元測試的過程中會對自己的代碼更了解,會更容易發現問題進而去完善;