—— 题目:
请编写一个能自动生成小学四则运算题目的 “软件”。
让程序能接受用户输入答案,并判定对错。
最后给出总共 对/错 的数量。
——需求分析:
- 累计每次测试答对,答错的题的个数
- 对用户给出的答案做出相应判断
- 显示答案的对错
——扩展功能:
- 程序可以求出带括号的四则运算
- 四则运算中允许有分数的出现
- 不仅仅只能做10道题目,可以无限制的做
——设计:
- 采用模块化思想,把源文件分成几个模块进行封装
- 生成算术表达式以及解析算术表达式
——代码实现:
main.cpp文件
#include "stackOperation.h"
#include "stdlib.h"
#include "stdio.h"
#include "charactorOperation.h"
#include "calculator.h"
#include<string.h>
void main(void)
{
long sum;
long sum_answer;
float answer;
int right = 0;
int wrong = 0;
while(1)
{
char *expression = generate(containNums(8));
sum=((int)(EvaluateExpression(expression)*1000));
if(sum%10 >= 5)
sum_answer = sum/10 + 1;
else
sum_answer = sum/10;
printf("%s=",expression);
scanf("%f",&answer);
long temp = answer*100;
if (temp == sum_answer)
{
right++;
}else
{
wrong++;
}
printf("
Right: %d,Wrong: %d",right,wrong);
}
system("pause");
system("cls");
}
calculator.cpp文件
#include "calculator.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//这个方法是产生多少个数字以内的运算表达式
int containNums(int num)
{
srand((unsigned)time(0));
return (rand()%(num - 2)) + 2;
}
char* generate(int count)
{
srand((unsigned)time(0));
int len_op = (count - 1)*3;
int repeat = 0;
int repeat_op = 0;
int turn = 0;
int i,j;
int *nums = (int *)malloc(sizeof(int)*count);
int *ops = (int *)malloc(sizeof(int)*len_op);
for (i = 0;i < count; i++)
{//产生-99到99的整数,0除外
int temp = -rand()%50 + 50;
if(temp != 0)
nums[i] = temp;
}
for (j = 0;j < len_op;j++)
{
int temp = rand()%6;
if (temp == 4 && repeat < count && ops[j - 1] != 5)
{
repeat++;
turn++;
ops[j] = temp;
continue;
}
if (temp == 5 && turn > 0 && ops[j - 1] != 4)
{
turn--;
ops[j] = temp;
continue;
}if (temp >= 0 && temp < 4)
{
repeat_op++;
if (repeat_op > count - 1)
{
break;
}
ops[j] = temp;
continue;
}
j--;
}
for(int h = 0 ;h < turn; h++)
{
ops[j] = 5;
j++;
}
for(int n = 0; n < j;n++)
{
if(ops[n] == 4 && ops[j - 1 - n] == 5)
{
ops = &ops[n + 1];
j--;
}else
break;
}
len_op = j;
char *result = (char *)malloc(sizeof(char) * (j + count + 1) * 5);
i = 0;
j = 0;
int state = 0;
int m;
int lower = 0;
for(m = 0; m < len_op + count*5 && j < len_op; m++)
{
if(state == 0 && ops[j] != 4)
{
if(nums[i] < 0)
{
lower = 1;
nums[i] = 0 - nums[i];
if(nums[i] > 9)
{
result[m++] = '(';
result[m++] = '-';
result[m++] = '0' + (nums[i]/10);
}
result[m++] = '0' + (nums[i++]%10);
result[m++] = ')';
}else
{
if(nums[i] > 9)
{
result[m++] = '0' + (nums[i]/10);
}
result[m++] = '0' + (nums[i++]%10);
}
state = 1;
}
switch(ops[j])
{
case 0:
result[m++] = '+';
break;
case 1:
result[m++] = '-';
break;
case 2:
result[m++] = '*';
break;
case 3:
result[m++] = '/';
break;
case 4:
if(j != 0)
{
if(lower == 1)
{
if(result[m - 2] >= 48 && result[m - 3] >= 48)
{
result[m] = result[m - 1];
result[m - 1] = result[m - 2];
result[m - 2] = result[m - 3];
result[m - 3] = result[m - 4];
result[m - 4] = result[m - 5];
result[m - 5] = '(';
break;
}if(result[m - 1] >= 48)
{
result[m] = result[m - 1];
result[m - 1] = result[m - 2];
result[m - 2] = result[m - 3];
result[m - 3] = result[m - 4];
result[m - 4] = '(';
}
lower = 0;
}
else
{
if(result[m - 1] >= 48 && result[m - 2] >= 48)
{
result[m] = result[m - 1];
result[m - 1] = result[m - 2];
result[m - 2] = '(';
break;
}if(result[m - 1] >= 48)
{
result[m] = result[m - 1];
result[m - 1] = '(';
}
}
}
else
result[m] = '(';
break;
case 5:
result[m] = ')';
break;
}
lower = 0;
if(ops[j] == 0 || ops[j] == 1 || ops[j] == 2 || ops[j] == 3)
{
if(nums[i] < 0)
{
lower = 1;
nums[i] = 0 - nums[i];
if(nums[i] > 9)
{
result[m++] = '(';
result[m++] = '-';
result[m++] = '0' + (nums[i]/10);
}
result[m++] = '0' + (nums[i++]%10);
result[m] = ')';
}else
{
if(nums[i] > 9)
{
result[m++] = '0' + (nums[i]/10);
}
result[m] = '0' + (nums[i++]%10);
}
}
j++;
}
result[m] = '#';
return result;
}
charactorOperation.cpp文件
#ifndef __CALCULATOR_H__ #define __CALCULATOR_H__ int containNums(int num); //几个数以内的运算,至少两个数 char* generate(int count); //生成算数表达式 #endif
calculator.cpp文件
#include "charactorOperation.h"
int JudgeOperator(char c)
{
if(c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')' || c == '#')
return 1;
else
return 0;
}
int Precede(char temp,char c)
{
if(temp=='+')
{
if(c=='+') return 1;
else if(c=='-') return 1;
else if(c=='*') return -1;
else if(c=='/') return -1;
else if(c=='(') return -1;
else if(c==')') return 1;
else return 1;
}
else if(temp=='-')
{
if(c=='+') return 1;
else if(c=='-') return 1;
else if(c=='*') return -1;
else if(c=='/') return -1;
else if(c=='(') return -1;
else if(c==')') return 1;
else return 1;
}
else if(temp=='*')
{
if(c=='+') return 1;
else if(c=='-') return 1;
else if(c=='*') return 1;
else if(c=='/') return 1;
else if(c=='(') return -1;
else if(c==')') return 1;
else return 1;
}
else if(temp=='/')
{
if(c=='+') return 1;
else if(c=='-') return 1;
else if(c=='*') return 1;
else if(c=='/') return 1;
else if(c=='(') return -1;
else if(c==')') return 1;
else return 1;
}
else if(temp=='(')
{
if(c=='+') return -1;
else if(c=='-') return -1;
else if(c=='*') return -1;
else if(c=='/') return -1;
else if(c=='(') return -1;
else return 0;
}
else if(temp==')')
{
if(c=='+') return 1;
else if(c=='-') return 1;
else if(c=='*') return 1;
else if(c=='/') return 1;
else if(c==')') return 1;
else return 1;
}
else
{
if(c=='+') return -1;
else if(c=='-') return -1;
else if(c=='*') return -1;
else if(c=='/') return -1;
else if(c=='(') return -1;
else return 0;
}
}
float Evaluate(float num1,float num2,char op)
{
float result;
switch(op)
{
case '+':
result=num2+num1;
break;
case '-':
result=num2-num1;
break;
case '*':
result=num2*num1;
break;
case '/':
result=num2/num1;
break;
}
return result;
}
float char_to_float(char *&p)
{
char *p_temp1;
char array[8]={' ',' ',' ',' ',' ',' ',' ',' '};
int i,n,j;
float number=0;
int radix=0;
p_temp1=p;
i=0;
j=0;
while (!JudgeOperator(*p_temp1))
{
array[i]=*p_temp1;
i++;
j++;
p_temp1++;
}
j--;
for (i=0;i<8;i++)
{
if (array[i]==' ')
{
break;
}
else
{
switch(array[i])
{
case '0':
radix=0;
break;
case '1':
radix=1;
break;
case '2':
radix=2;
break;
case '3':
radix=3;
break;
case '4':
radix=4;
break;
case '5':
radix=5;
break;
case '6':
radix=6;
break;
case '7':
radix=7;
break;
case '8':
radix=8;
break;
case '9':
radix=9;
break;
}
if (j==0)
{
radix=radix*1;
}
if (j>0)
{
for (n=j;n>0;n--)
{
radix=radix*10;
}
}
number=number+radix;
j--;
}
}
p=p_temp1;
return number;
}
charactorOperation.h文件
#ifndef __CHARACTOR_H__ #define __CHARACTOR_H__ int JudgeOperator(char c); int Precede(char temp,char c); float Evaluate(float num1,float num2,char op); float char_to_float(char *&p); #endif
stackOperation.cpp文件
#include "stackOperation.h"
#include "charactorOperation.h"
#include <stdlib.h>
#include <stdio.h>
int init_char_stack(char_stack &s)
{
s.base=(char *)malloc(100*sizeof(char));
if(!s.base)
exit(0);
s.top=s.base;
s.stacksize=100;
return 1;
}
int init_float_stack(float_stack &s)
{
s.base=(float *)malloc(100*sizeof(float));
if(!s.base)
exit(0);
s.top=s.base;
s.stacksize=100;
return 1;
}
int empty_char_stack(char_stack s)
{
if(s.top==s.base)
return 1;
else
return 0;
}
int empty_float_stack(float_stack s)
{
if(s.top==s.base)
return 1;
else
return 0;
}
char get_char_top(char_stack s, char &e)
{
if(s.base==s.top)
{
printf("此栈为空!");
return 0;
}
e=*(--s.top);
return e;
}
float get_float_top(float_stack s, float &e)
{
if(s.base==s.top)
{
printf("此栈为空!");
return 0;
}
e=*(--s.top);
return e;
}
int char_push (char_stack &s,char e)
{
if (s.top-s.base>=s.stacksize)
{
s.base =(char *)realloc(s.base,(s.stacksize+10)*sizeof(char));
if(!s.base)
exit(0);
s.top=s.base+s.stacksize;
s.stacksize+=10;
}
*s.top++=e;
return 1;
}
float float_push (float_stack &s,float e)
{
if (s.top-s.base>=s.stacksize)//栈满,追加存储空间
{
s.base =(float *)realloc(s.base,(s.stacksize+10)*sizeof(float));
if(!s.base)
exit(0);
s.top=s.base+s.stacksize;
s.stacksize+=10;
}
*s.top++=e;
return 1;
}
char char_pop(char_stack &s,char &e)
{
if (s.base==s.top)
{
printf("此栈为空!");
return 0;
}
s.top--;
e=*s.top;
return e;
}
float float_pop(float_stack &s,float &e)
{
if (s.base==s.top)
{
printf("此栈为空!");
return 0;
}
s.top--;
e=*s.top;
return e;
}
float EvaluateExpression (char *s)
{
char_stack OPTR;//操作符栈
float_stack OPND;//操作数栈
float a,b;
float final;//存放最后的结果
float num;
char c;
char temp;//暂存栈顶元素
char op;//暂存操作符
char *p_s1;//读取字符串的指针
p_s1=s;
init_char_stack(OPTR);
char_push(OPTR,'#');
init_float_stack(OPND);
c=*p_s1;
while (c!='#' || get_char_top(OPTR,temp)!='#')
{
if (!JudgeOperator(c))
{
num=char_to_float(p_s1);
float_push(OPND,num);
c=*p_s1;
}
else//若为运算符
{
switch(Precede(get_char_top(OPTR,temp),c))
{
case -1:
if (get_char_top(OPTR,temp) == '(' && c == '-')
{
float_push(OPND,0);
}
char_push(OPTR,c);
p_s1++;
c=*p_s1;
break;
case 0:
char_pop(OPTR,c);
p_s1++;
c=*p_s1;
break;
case 1:
char_pop(OPTR,op);
float_pop(OPND,a);
float_pop(OPND,b);
float_push(OPND,Evaluate(a,b,op));
break;
}
}
}
*p_s1 = ' ';
final=get_float_top(OPND,final);
return final;
}
stackOperation.h文件
#ifndef __STACK_H__
#define __STACK_H__
typedef struct
{
char *base; //指向栈顺序存储结构数组的指针
char *top; //栈顶指针,指向栈顶元素下一个位置
int stacksize; //当前已分配的存储空间,即栈的最大容量,以元素为单位
}char_stack;//操作符栈
typedef struct
{
float *base;
float *top;
int stacksize;
}float_stack;//操作数栈
int init_char_stack(char_stack &s);
int init_float_stack(float_stack &s);
int empty_char_stack(char_stack s);
int empty_float_stack(float_stack s);
char get_char_top(char_stack s, char &e);
float get_float_top(float_stack s, float &e);
int char_push (char_stack &s,char e);
float float_push (float_stack &s,float e);
char char_pop(char_stack &s,char &e);
float float_pop(float_stack &s,float &e);
float EvaluateExpression (char *s);
#endif
两人合作步骤:
1. 首先我们先对做的题目读得不太清楚, 题目中的请采用模块化设计思想,修改之前的code,将 “计算功能” 封装起来,真的是不理解。
2. 然后我们通过向同学询问和上网查找资料才知道老师大概要求我们做的是什么,但是这个时候我们两个又意识到我们的代码又出现了新的问题。在第二次作业中我们两个写的代码存在问题,觉得缺乏模块化的思想,自己读起来都觉得很乱。层次不是很清楚,不像其它同学写的一样,主函数和子函数分得很清楚。这样无论是将“计算功能”封装起来,还是对其进行单元测试,实现起来都方便很多。
3.为了更好地实现这次作业的要求,我们两个在商量以后,就觉定重新写代码,并进行规范以及相应的修改。这是一个相当痛苦的过程,什么都是从零开始。
4. 通过老师在课堂上的讲解,大概地知道怎么让自己的代码看起来更加地规范,怎么进行但单元测试。知道了头文件(.h)和源文件(.c)中存放的是些什么内容。
5.对函数进行模块化,将函数进行了封装,并把函数声明与具体实现分别存放在头文件(.h)和源文件(.c)中。
6.代码规范设计方面,我们认真阅读教材Page57:4.2-4.3节中内容,进行了风格、设计讨论,了解之后,一人说一人写。
总结:
1.刚刚拿到这个题的时候是蒙的,作业里面的要求都是在原来的学习过程中没有接触到的。比如说怎么进行封装。
2.在老师的讲解下,大概知道了函数封装和测试的基本思想。
3.对于这次作业的完成感觉很吃力,消耗了大量的时间。
4.上次作业因为写得不规范,给这次作业的完成增加了工作量。
5.编程的过程中,模块化思想很重要。遇到困难时,要懂得查阅资料和同学进行交流。
PSP耗时统计
| 序号 | psp | 耗时(h) | 百分比(%) |
| 1 | 需求分析 | 5 | 9.0 |
| 2 | 设计 | 10 | 18.18 |
| 3 | 代码实现 | 24 | 43.6 |
| 4 | 测试 | 12 | 21.8 |
| 5 | 分析和总结 | 4 | 7.2 |
运行的效果图:
