軟件工程第3次作業—四則運算(結對作業)
作業要求的博客鏈接:https://edu.cnblogs.com/campus/nenu/2016CS/homework/2266
git倉庫地址:https://git.coding.net/pipifan/f4.git
本次作業是結對作業,我的結對夥伴是樊友朋同學,他的博客地址是:http://www.cnblogs.com/pipifan/p/9918250.html
項目概要:
本次項目實現的是一個用於四則運算的控制臺程序,目前已實現的功能如下:
1)支持整數和不含括號的四則運算且表達式可以重復。
2)支持小數和含小括號的四則運算且表達式可以重復。
3)表達式不重復且輸出結果顯示在控制臺,然後將控制臺顯示的結果輸出到指定位置的txt文件中。
項目詳情:
一、本次作業采用c/c++進行編程。首先分析項目要求概括功能:
1)按照控制臺輸入的N ,生成N道由隨機生成的整數與合法運算符組成的四則運算,並判斷用戶輸入答案的對錯。(輸入 f4 -n N)
2)按照控制臺輸入的N ,生成N道由隨機生成的整數、小數與合法運算符和括號組成的四則運算,並判斷用戶輸入答案的對錯。(輸入 f4 -c N )
3)按照控制臺輸入的N 、文件路徑M,生成N道由隨機生成的整數、小數與合法運算符和括號組成的不重復的四則運算,給出答案打印在控制臺,輸出到文件M中。(輸入 f4 -c N -f M)
4)如果輸入的N不合法會輸出相應的警告。
二、根據分析規劃函數模塊:
1)字符串轉化為數字。
2)判斷運算符號的優先級。
3)處理四則運算表達式,計算表達式的值。
4)處理控制臺輸入輸出,產生隨機數,隨機運算符號和隨機括號。
5)主函數,負責調用各個模塊,控制流程。
三、部分模塊代碼實現過程:
1)處理四則運算表達式。
首先分配兩個棧sp,sv,分別存儲運算符和運算數字,從左到右讀取中綴表達式,遇到操作數就入棧sv,遇到左括號或者當前運算符比sp棧頂優先級高的符號就入棧sp,遇到比sp棧頂優先級低的符號就出棧兩個操作數和棧頂操作符,結果再入棧sv,遇到右括號同時棧頂是左括號就出棧左括號。最後sv棧頂就是該表達式的結果。
具體代碼如下:
double solve(string s ) { stack<double> sv; stack<char> sp; char c; int k = 0, flag = 1; double x, y; sp.push(‘\0‘); c = s[k]; while (flag) { if (c >= ‘0‘&&c <= ‘9‘ || c == ‘.‘) { sv.push(toNum(s, k)); } else if (c == ‘\0‘&& sp.top() == ‘\0‘) { flag = 0; } else if (c == ‘(‘ || (priority(c) > priority(sp.top()))) { sp.push(c); k++; } else if (c == ‘)‘&& sp.top() == ‘(‘) { sp.pop(); k++; } else if (priority(c) <= priority(sp.top())) { x = sv.top(); sv.pop(); if(sv.empty()) y = 0; else { y = sv.top(); sv.pop(); } c = sp.top(); if( c == ‘/‘ && fabs( x ) <= eps ){ int num = rand() % 3; c = mp[num]; } sp.pop(); switch (c) { case ‘+‘:y = x + y; break; case ‘-‘:y = y - x; break; case ‘*‘:y = x*y; break; case ‘/‘:y = y / x; break; } sv.push(y); } c = s[k]; } return sv.top(); }
2)隨機生成整數數字和運算符。
實現功能一用rand隨機生成數字和運算符(提前存到數組中)。
這裏在實現過程中有兩點我們進行了思考:其一是單純用rand()會使每次的隨機數一樣,這樣就不符合隨機這個概念,所以我們加上 srand( (int)time(0) ),用時間做隨機數的種子,這樣可以保證每次的隨機數不同。其二是考慮到除數不能為0,所以我們判斷如果“/”後面的隨機數是0就再造數據直至不為0,保證了表達式的正確性。
s = ""; if( flag == 1 ){ for(int i = 0 ; i < 7 ; i++ ){ if( (i % 2) == 0 ){ int num = rand() % 10; int len = s.size(); while( len > 0 && num == 0 && s[len - 1] == ‘/‘ ) num = rand() % 10; stringstream ss; ss << num; string tmp; ss >> tmp; s += tmp; } else{ int num = rand() % 4; s += mp[num]; } } }
3)隨機生成帶括號的整數和小數的數據。
生成小數時用兩個隨機數相除。
這裏的難點是隨機加括號,我們采取的辦法是先隨機加左右括號,最後檢查是否匹配,缺少匹配的括號就在表達式首尾加上相應的括號。
for(int i = 0 ; i < 7 ; i++ ){ string tmp; if( (i % 2) == 0 ){ int num = rand() % 10; if( (num % 3) == 0 && num > 0 ){ int num1 = rand() % 100; int num2 = rand() % 100; while( num2 == 0 ) num2 = rand() % 100; double tmp_num = num1*1.0 / num2; stringstream ss; ss << tmp_num; ss >> tmp; x[xx++] = tmp_num; } else{ int len = s.size(); while( len > 0 && num == 0 && s[len - 1] == ‘/‘ ) num = rand() % 10; stringstream ss; ss << num; ss >> tmp; x[xx++] = num*1.0; } if( num < 3 && i < 6 && i > 0 ){ s += "("; cnt1++; } s += tmp; if( num > 6 && i > 0 && i < 6){ if( cnt1 > 0 ) cnt1--; else cnt2++; s += ")"; } } else{ int num = rand() % 4; s += mp[num]; } }
四、測試階段:
1)功能1
2)功能2
3)功能3
五、項目進行過程
1.給出結對編程的體會
結對編程的好處是可以集思廣益,尤其針對本次作業的一些小的細節可以做到查漏補缺。
比如輸入參數錯誤時的文字提示,功能一和功能二的提示是不同的;比如除數不能為0;比如保留小數的位數時要考慮是否為有限小數,無限小數要保留三位;比如功能三輸出時要對齊......等等的一些問題。在解決功能二中如何隨機加括號,並且如何匹配正確時,兩個人也進行了各自的思考然後加以討論得出解決方案。在查找相應的參考資料時也更便捷。
由於結對編程需要共用一個設備,所以編程以平時寫代碼能力比較強的樊友朋同學為主,我負責收集整理資料、提供思路,發現、修改細節問題和測試數據。我對結對編程的體會應該是分清主次,各司其職,需要默契,在討論中解決問題和完善項目。
2. 至少3項在編碼、爭論等活動中花費時間較長,給你較大收獲的事件。
1). 用棧處理四則運算表達式。具體思路可以參考3.1
2). 隨機生成小數和括號。主要的難點是隨機加括號,並且要保證括號的正確性。具體思路參考3.3
3). 功能二和三要求表達式不重復。初看作業要求中的不重復概念是要運用交換律、結合律等數學定理來檢測題的不重復性,但是這樣比較麻煩。所以經過討論後,我的夥伴提出為了保證題的不重復,可以檢查每個題中的數字是否相同。因為題目本身是由隨機數、隨機括號、隨機運算符組成的,它重復的概率很低,所以我們把表達式的數字排序,包裝放在set裏面,檢查數字重復時就重新生成表達式,這樣不會影響隨機性,也不會有重復的表達式,最重要的是降低算法復雜度(相對檢查交換律的那種算法)
4). 花費時間長的地方還有5.1提到的細節問題,為了盡力滿足作業要求,我們用了大概兩至三個小時去修改細節。
六、給出照片1張,包括結對的2位同學、工作地點、計算機,可選項包括其他能表達結對編程工作經歷的物品或場景。
軟件工程第3次作業—四則運算(結對作業)