经过多个礼拜的编程,最终终于完成了结对项目。
本次结对项目是基于上次的个人项目完成的。本质上来说,是将上回的计算功能模块化,再加入随机生成算式的功能模块,最后把他们附在界面上调用。
完成项目
1、随机生成算式
2、能选择生成算式的长度
3、能够选择生成算式中数字的位数和精度
4、能够选择生成的括号数量(因为考虑到随机生成,所以控制的数量是括号的最大量而不是固定量)
完整代码
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<time.h> #include<stack> using namespace std; #define N 1000 int timer=0; int ri,wr=0; int chance=3; char infix[N]; //中缀表达式(未分离,都在一个字符串里) char infix2[N]; char expression[N][10]; //保存预处理过的表达式,也就是每个元素都分离过的表达式 char suffix[N][10]; //保存后缀表达式的操作数 int count;//表达式中元素的个数(一个完整到数字(可能不止一位数)或者符号) int suffixLength;//后缀表达式的长度 struct fra{ char str[10];//为了在栈中记录符号,所以包括数字全部用char记录 double up;//分子 double down;//分母 }; int len = 0; FILE *fp = fopen("1.txt", "r"); long answer; fra answer1,answer2; class cal{ private: int level(char a){ switch(a){ case '#':return 0; case '+': case '-':return 1; case '*': case '/':return 2; case '^':return 3; default:break; } return -1; } int isDigital(char x){ if( (x>='0'&&x<='9') ) return 1; return 0; } int isNumber(char *str){ int i; for(i=0;str[i];i++){ if(isDigital(str[i])==0)return 0; } return 1; } /************************************* 预处理中缀表达式,把连续的字符分离成不同的元素,用字符串数组(expression[][]) 保存,方便后面的计算,因为这里考虑了运算数可能不全是个位数 比如:(12+3) 在处理成后缀表达式时,是123+,容易产生歧义(1+23 ? 12+3) *************************************/ void pretreatment(char *str){ int i,j,numberFlag; char temp[3]; char number[10]; count=0; numberFlag=0; for(j=0,i=0;str[i];i++){ if(isDigital(str[i])==0){ if(numberFlag==1){ number[j]=0; strcpy(expression[count++],number); j=0; numberFlag=0; } if(str[i]!=' '){ temp[0]=str[i];temp[1]=0; strcpy(expression[count++],temp); } } else { numberFlag=1; number[j++]=str[i]; } } // puts("分离后的表达式为"); // for(i=0;i<count;i++){ // printf("%s ",expression[i]); // }puts(""); // puts(""); } /***************************************** 中缀表达式 转 后缀表达式 遍历字符串,对于str[i] str[i]是运算数(或者是字母代替的运算变量)输出; str[i]是符号,有两种情况 (1),是右括号,栈顶元素输出,直到与str[i]匹配的左括号出栈(左括号不用输出打印) (2),是运算符,判断str[i]与栈顶元素的优先级,str[i]优先级 不高于 栈顶符号,则栈 顶元素输出,直到栈空 或者 栈顶符号优先级低于str[i] *****************************************/ void infix_to_suffix(char str[N][10]){ memset(suffix,0,sizeof(suffix)); suffixLength=0; stack <char*> st; int i=0; char Mark[2]="#"; st.push(Mark); do{ if(isNumber(str[i])==1)//运算数直接保存到后缀表达式中 strcpy(suffix[suffixLength++],str[i]); else if(str[i][0]=='(') //是 左括号,直接入栈 st.push(str[i]); else if(str[i][0]==')'){ //是 右括号,栈顶出栈,直到与其匹配的左括号出栈 while( strcmp(st.top(),"(")!=0 ){ char temp[10]; strcpy(temp,st.top()); strcpy(suffix[suffixLength++],temp); st.pop(); } st.pop(); } else if( strcmp(st.top(),"(")==0 )//是 运算符,且栈顶是左括号,则该运算符直接入栈 st.push(str[i]); else { //是 运算符,且栈顶元素优先级不小于运算符,则栈顶元素一直 //出栈,直到 栈空 或者 遇到一个优先级低于该运算符的元素 while( !st.empty() ){ char temp[10]; strcpy(temp,st.top()); if( level(str[i][0]) > level(temp[0]) ) break; strcpy(suffix[suffixLength++],temp); st.pop(); } st.push(str[i]); } i++; }while(str[i][0]!=0); while( strcmp(st.top(),"#")!=0 ){ //将栈取空结束 char temp[10]; strcpy(temp,st.top()); strcpy(suffix[suffixLength++],temp); st.pop(); } // puts("后缀表达式为:"); // for(i=0;i<suffixLength;i++){ // printf("%s",suffix[i]); // }puts(""); // puts(""); } ////////////////////////////////////////////////// int gcd(int a,int b) { //最大公约数 if(b == 0) return a; else return gcd(b,a % b); } void simplify(fra aaa){ int a=gcd(aaa.down,aaa.up); aaa.up=aaa.up/a; aaa.down=aaa.down/a; // printf("%.1lf/%.1lf\n",aaa.up,aaa.down); // puts("计算出后缀表达式的结果:"); /* if(aaa.down==1) { printf("%lf\n",aaa.up); } else { printf("%lf/%lf\n",aaa.up,aaa.down); } */ if(timer==0) { answer1.up=aaa.up; answer1.down=aaa.down; timer++; } else { answer2.up=aaa.up; answer2.down=aaa.down; timer--; } // printf("%d\n%lf %lf\n%lf %lf\n",timer,answer1.up,answer1.down,answer2.up,answer2.down); } /************************************** 计算后缀表达式的值 **************************************/ fra kt[N]; int stackTop; void getResult(char str[N][10]){ // fra fras[N]; stackTop=0; /*这里要注意,内存的分配方案导致 i 的位置就在temp[9]旁边,然后strcpy()函数直接拷贝内存的话,在temp越界情况下会覆盖 i 的值*/ int i; // double temp; for(i=0;i<suffixLength;i++){ if(isNumber(str[i])==1){ // strcpy(kt[stackTop++].str,str[i]); kt[stackTop].up=atof(str[i]); kt[stackTop].down=1; stackTop++; } else { // char a[10],b[10]; fra na,nb,nc; // strcpy(a,kt[stackTop-1].str); na.up = kt[stackTop-1].up; na.down=kt[stackTop-1].down; //fras[stackTop-1].up=na; //fras[stackTop-1].down=1; stackTop--; // strcpy(b,kt[stackTop-1].str); nb.up = kt[stackTop-1].up; nb.down=kt[stackTop-1].down; //fras[stackTop-1].up=nb; //fras[stackTop-1].down=1; stackTop--; if(str[i][0]=='+') { nc.down=nb.down*na.down; nc.up=nb.up*na.down+na.up*nb.down; } else if(str[i][0]=='-') { nc.down=nb.down*na.down; nc.up=nb.up*na.down-na.up*nb.down; } else if(str[i][0]=='*') { nc.up=nb.up*na.up; nc.down=nb.down*na.down; } else if(str[i][0]=='/') { nc.down=nb.down*na.up; nc.up=nb.up*na.down; } // sprintf(temp,"%lf",nc); //strcpy(kt[stackTop++].str,temp); kt[stackTop].down=nc.down; kt[stackTop].up=nc.up; stackTop++; } } sprintf(kt[stackTop-1].str,"%lf",kt[stackTop-1].up/kt[stackTop-1].down); //strcpy(kt[stackTop++],temp); /* char as[N]; gets(as); if(as==kt[stackTop-1].str) puts(" right"); else{ puts("wrong"); } */ simplify(kt[stackTop-1]); for(i=0;i<suffixLength;i++){ memset(infix,0,sizeof(infix)); memset(suffix,0,sizeof(suffix)); memset(expression,0,sizeof(expression)); } } // char szTest[1000] = {0}; void checkanswer() { // getchars(); // printf("%d/%d\n%d/%d",answer1.up,answer1.down,answer2.up,answer2.down); if(answer1.up==answer2.up&&answer1.down==answer2.down) { printf("right\n"); ri++; } else{ printf("wrong"); if(answer1.down!=1) { printf("\nthe answer is %lf/%lf\n",answer1.up,answer1.down); } else{ printf("\nthe answer is %lf\n",answer1.up); }wr++; } } public: void getanswer(char str[N]){ // gets char temp[N]; strcpy(temp,str); pretreatment( strcat(temp," ") ); infix_to_suffix(expression); getResult(suffix); checkanswer(); printf("正确回答%d题\n错误回答%d题\n",ri,wr); } void work(char str[N]) { char temp[N]; // while(gets(infix)){ // getchars(); // while(z<3){ // if(!feof(fp)) // { // memset(infix, 0, sizeof(infix)); // fgets(infix, sizeof(infix) - 1, fp); // 包含了\n // printf("%s", infix); // } // getchars(); // printf("%s",str); strcpy(temp,str); pretreatment( strcat(temp," ") ); infix_to_suffix(expression); getResult(suffix); // getchars(); } }; class tuxinghua{ private: int kh;//括号总数 int stime;//左括号剩余可生成次数 int lef; void fcheck(char str[N],int x){//符号检查 char fuhao[2]; // srand((unsigned)time(NULL)); if(x>=0&&x<=21) {strcpy(fuhao,"+");} else if(x>=22&&x<=43) {strcpy(fuhao,"-");} else if (x>=44&&x<=65) {strcpy(fuhao,"*");} else if (x>=66&&x<=87) {strcpy(fuhao,"/");} // else // {printf("括号");} strncat(str,fuhao,1); } void ncheck(char str[N],int x){//数字检查 char shuzi[2]; int as; // Sleep(10); // srand((unsigned)time(NULL)); for(int i=0;i<x;i++) { // int n=rand()%10; as=rand()%10; // printf("\n%d\n",rand()%100); sprintf(shuzi, "%d", as); strncat(str,shuzi,1); } } void left(char str[N]) { // int i=x; char zuo[2]; int b=rand()%3; if(b==1&&stime<=kh&&stime>=1){ strcpy(zuo,"("); strncat(str,zuo,1); // strncat(str,"(",2); //kh--; lef++; stime--; } } void right(char str[N]){ int b=rand()%5; char zuo[2]; if(b==1&&lef>=1){ strcpy(zuo,")"); strncat(str,zuo,1); // kh--; lef--; } } public: int tgetchars(char str[N]){ if( gets(str)) return 1; else return 0; } int shengcheng(char str[N]){ int clen; int max; int a=0; int f; lef=0; printf("输入长度(奇数)"); scanf("%d",&clen); printf("数字取值范围(位数)"); scanf("%d",&max); printf("最多括号个数"); scanf("%d",&kh); stime=kh; srand((unsigned)time(NULL)); while(1) { while(a<clen) { f=rand()%88; if(a%2==0) { left(str); ncheck(str,max); } else { right(str); fcheck(str,f); } a++; } if(lef==0) break; else { // printf("aa"); lef=0; memset(infix,0,sizeof(infix)); a=0; stime=kh; continue; } } return 1; } }; void main(){ cal ca; tuxinghua x; //char infix[N]; // getc a; int z=0; while(1){ x.shengcheng(infix); printf("%s=\n",infix); ca.work(infix); // x.tgetchars(infix); // gets(infix); scanf("%s",&infix); // printf("%s=\n",infix); // ca.work(infix); ca.getanswer(infix); // z++; } }
本次实验我感悟最深的是项目设计前的规范性。
因为第一次个人项目时没有一个详细的规划,所以在将其模块化时遇到了很大的困难。因此不得不对于整体结构进行了一定的调整。
即便如此,在与生成控制函数耦合的时候,也不得不将一些接口变成了全局变量,来减少代码的改变量。这极大的降低了代码模块化之后的通用性。
同时,因为在完成mfc界面前没有一个详细的规划,所以也遇到了很大的困难。
除此之外,也感到了沟通的重要性,因为在代码编写的过程中,没能提前统一代码的规范和结构,结果在开始编写后遇到了很多问题,让进度不快反慢,有一种“单干反而更轻松”的想法。