要求0:html
做業地址:https://edu.cnblogs.com/campus/nenu/2016CS/homework/2266
git
要求1:算法
git倉庫地址:https://git.coding.net/wangc556/f4.git
編程
要求2:函數
結對同窗博客地址:https://www.cnblogs.com/wangcwcgnaw/p/9926468.html 性能
結對同窗名字:王儲 2016011925學習
解題思路:編碼
根據王儲同窗的提示,四則運算求值必定要用到逆波蘭表達式,因此我百度搜了一下逆波蘭表達式的做用和用法。spa
將一個普通的中序表達式轉換爲逆波蘭表達式的通常算法是:.net
(1)首先構造一個運算符棧,此運算符在棧內遵循越往棧頂優先級越高的原則。
(2)讀入一個用中綴表示的簡單算術表達式,爲方便起見,設該簡單算術表達式的右端多加上了優先級最低的特殊符號「#」。
(3)從左至右掃描該算術表達式,從第一個字符開始判斷,若是該字符是數字,則分析到該數字串的結束並將該數字串直接輸出。
(4)若是不是數字,該字符則是運算符,此時需比較優先關係。
作法以下:將該字符與運算符棧頂的運算符的優先關係相比較。若是,該字符優先關係高於此運算符棧頂的運算符,則將該運算符入棧。假若不是的話,則將棧頂的運算符從棧中彈出,直到棧頂運算符的優先級低於當前運算符,將該字符入棧。
(5)重複上述操做(3)-(4)直至掃描完整個簡單算術表達式,肯定全部字符都獲得正確處理,咱們即可以將中綴式表示的簡單算術表達式轉化爲逆波蘭表示的簡單算術表達式。
以後的每一個功能在使用逆波蘭表達式的基礎上又作了一些細節的改動。
每一個功能重、難點:
功能一:實現運算數爲整數的不含小括號的四則運算,運算符具備優先級,對優先級進行規定。
功能二:在功能一的基礎上支持了小括號,括號參與運算後是考慮括號的匹配和生成順序,定義temp爲未匹配的左括號數來解決括號匹配問題。同時考慮運算數據爲浮點型,判斷結果爲無限小數仍是有限小數很困難。
功能三:生成的四則運算表達式是不重複的而且工整輸出。由於表達式重複的狀況太多因此我認爲這是一大難點,也是咱們不太知道如何解決的。
1.進行題目數、功能、文件路徑等變量定義
int main(int argc,char* argv[]){ string ss;//題目數的字符串 int qwq=0;//功能 string path;//輸出的文件路徑 for(int i=1;i<argc;++i){ string str=argv[i]; if(str=="-n"||str=="-c"){ ss=argv[i+1]; if(str=="-n") qwq=1; else qwq=2; } if(str=="-f") qwq=3,path=argv[i+1]; } int num=0,l,cnt=0;//num問題數,cnt回答正確數 //把問題數字符串轉化爲整數 l=ss.size(); for(int i=0;i<l;++i){ num*=10; num+=ss[i]-'0'; } //每次生成的隨機數都不一樣 srand((int)time(0));
2.生成運算式,使用rand()函數,功能一隨機生成1-10的整數,功能2、三生成0.01-10.00的浮點數
string s="";//生成的運算式的字符串 int temp=0;//未匹配的左括號數 //生成運算式 if(qwq==1){ for(int i=1;i<=4;++i){ if(i==1){ int r1=rand2(1,10); //整數轉字符串 stringstream ssss; ssss<<r1; string t; ssss>>t; s+=t; } else{ int r1=rand2(1,5); if(r1==1) s+="+"; if(r1==2) s+="-"; if(r1==3) s+="*"; if(r1==4) s+="/"; int r2=rand2(1,10); stringstream ssss; ssss<<r2; string t; ssss>>t; s+=t; } } }
定義temp爲當前位置未被匹配的左括號數量,在以後的位置以temp爲上界,將未匹配的的括號進行匹配,保證括號合法。
else{ for(int i=1;i<=5;++i){ if(i==1){ int r1=rand2(0,2); for(int i=0;i<r1;++i) s+="("; temp+=r1;//temp:未匹配的左括號數 double r2=rand1(1,1000); stringstream ssss; ssss<<r2; string t; ssss>>t; s+=t; } else if(i==5){ for(int i=0;i<temp;++i) s+=")"; temp=0; } else{ int r1; //生成右括號 if(temp){ r1=rand2(0,temp); for(int i=0;i<r1;++i) s+=")"; temp-=r1; } int r2=rand2(1,5); if(r2==1) s+="+"; if(r2==2) s+="-"; if(r2==3) s+="*"; if(r2==4) s+="/"; //生成左括號 r1=rand2(0,2); for(int i=0;i<r1;++i) s+="("; temp+=r1; double r3=rand1(1,1000); stringstream ssss; ssss<<r3; string t; ssss>>t; s+=t; } } }
3.定義兩個棧運算符棧和數字棧 應用逆波蘭表達式
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(to_num(s,k)); else if(c=='\0'&&sp.top()=='\0') flag=0; else if(c=='('||(level(c)>level(sp.top()))){ sp.push(c); k++; } else if(c==')'&&sp.top()=='('){ sp.pop(); k++; } else if(level(c)<=level(sp.top())){ x=sv.top(); sv.pop(); if(sv.empty()) y=0; else{ y=sv.top(); sv.pop(); } c=sp.top(); 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]; } double ans=sv.top();//運算式的值
4.在功能三上咱們不太知道如何避免重複,因此採用map映射來存儲結果判斷結果是否出現過,以保證表達式不一樣
//四捨五入 int dd=ans*10000; if(dd%10>=5) dd=dd-dd%10+10; else dd=dd-dd%10; ans=dd/10000.0; dd/=10000.0; if(mp[ans]){ j--; continue; } else mp[ans]++;
編程體會:
因爲本身的編程能力不強,因此我找了編程能力較強的王儲同窗做爲隊友,但願能夠在結對編程的過程當中學習到一些知識,提升本身。謝謝王儲同窗對我提出的疑問耐心解答,對我不會的知識點進行講解,我以爲本身在這個過程當中學到了不少有了一些提升。
我在結對編程以前讀了《構建之法》第四章兩人合做這一部分知識,鄒欣老師說,在結對編程中,任何一段代碼都至少被兩雙眼睛看過,被兩個腦殼思考過,結對編程避免了「個人代碼」仍是「他的代碼」的問題,使而整個代碼的責任不屬於某我的,而是屬於兩我的,進而屬於整個團隊。還有結對編程中駕駛員和領航員的角色要常常互換,避免長時間緊張工做而致使的觀察力和判斷力降低。因此我認爲結對編程仍是頗有必要的,對於一個好性能的軟件或者程序,我感受結對編程是不可缺乏的。
3項在編碼、爭論等活動中花費時間較長,給你較大收穫的事件:
(1)定義兩個棧 使用逆波蘭表達式
(2)括號匹配 判斷數據是否有限/無限循環並四捨五入保留三位小數
(3)如何判斷重複的表達式並再也不計算重複的式子
收穫:在此過程當中學到了不少編程思想,在隊友的講解下感受方法很巧妙。