一、实验目标
2)进一步提高个人编程技巧与实践。
二 、实验内容
1)根据以下问题描述,练习结对编程(pair programming)实践;
2)要求学生两人一组,自由组合。每组使用一台计算机,二人共同编码,完成实验要求。
3)要求在结对编程工作期间,两人的角色至少切换 4 次;
4)编程语言不限,版本不限。建议使用 Python 或 JAVA 进行编程。
队员博客地址:
王锦(https://www.cnblogs.com/wjin/) 张子毓(
https://www.cnblogs.com/lingluan23333/) 四则运算
github仓库地址
https://github.com/Wjinfiny/daimaqiaobu
三、实验过程
1.确定运算式长度——定长或随机
定长:简单
假设两个运算符,不需要堆栈,前后两个运算符比较,优先级高的先运算,结果再与剩下的数运算即可;如果是三个,也可以用这个方法,相对于堆栈都相对简单
随机:使用堆栈;随机又可以分情况(手动输入运算式或随机产生运算式)
{
A.手动输入
B.随机产生 随机产生式子长度,随机产生运算式和运算符
}
经过讨论我们认为定长相对简单,没有挑战性;而随机产生运算式较为复杂,有两个随机需要处理。在时间相对不是十分充裕的情况下,我们选择了中间难度
![](https://img2020.cnblogs.com/blog/1946335/202003/1946335-20200328133834184-403805854.png)
4.根据功能划分模块,定义接口
A.功能模块图
B.函数名(接口)
1.OperateNum:运算数
2.OperateSymbol:运算符
3.GetOperateNum:取数
4.GetOperateSymbol:取符
5.PushOperateNum:数进栈
6.PushOperateSymbol:数进栈
7.PopOperateNum:数退栈
8.PopOperateSymbol:符退栈
9.IsOperateSymbolOrNum(char ch):判断输入的符号是否是四则运算符号
10.Priority(char inputnum, char ch):判断符号的优先级
11.Calculate(int num1, char ch, int num2):两数运算
12.MainCalc()//于用户输入和计算结果
13.int main()//主函数
五、实验遇到的困难及交流讨论
1.角色分工
上述函数1、2、3、4、5、6、7、8、12是我编写的,9、10、11、13为张子毓编写的
我先编写1~8,运算数和运算符的定义及简单的入栈出栈销毁栈取栈顶;张子毓编写判断输入的符号是否是四则运算符号、判断符号的优先级、两数运算;再次交换角色,我编写MainCale()函数(于用户输入和计算结果)整合张子毓前面的编写的三个函数;最后张子毓编写主函数。
2.遇到的问题及讨论
A.我先屏幕分享,一起分析了题目的几种选择(如定长随机长度),进行讨论确定类型。这里我们犯了一个很蠢的错误,我们一开始选择的是随机长度手动输入。我们设定了随机函数,产生随机式子长度p(0<p<=4)。代码编写的中途,在张子毓的提醒下,突然醒悟,手动输入就是随机式子长度。
B.在输入式子的过程中,出现如下错误
(这是正确代码完成后的错误还原,原来的错误更严重,只能输入第一个式子,其他式子没有办法输入)
我们修改了好久,以为是MainCala()函数(计算答案的函数)调用出了问题,后来反复修改,使用printf找错误,最后发现是错把回车当成了输入字符,修改代码添加了getchar()
C.两数运算这部分是张子毓写的,但一开始她没有想到不符合题目规范的式子不予计算的情况。角色互换后,我修改代码,将两数运算中会出现不规范的“-”和“/”做出了修改,设置出错情况下result=-1000,这样就主函数进行判断,就不会计算不规范的式子
六,代码及运行截图及托管代码
A.源代码
#include <stdlib.h>
#define MAX 20
//定义两个栈,分别存放运算数和运算符
struct SNode_Num
{
int datas[MAX];
int top;
};
typedef struct SNode_Num OperateNum;
struct SNode_Symbol
{
char symbol[MAX];
int top;
};
typedef struct SNode_Symbol OperateSymbol;
//取出相应的数
int GetOperateNum(OperateNum *StackNum)
{
return StackNum->datas[StackNum->top];
}
//取出相应运算符
char GetOperateSymbol(OperateSymbol *StackSymbol)
{
return StackSymbol->symbol[StackSymbol->top];
}
//运算数进栈
void PushOperateNum(OperateNum *StackNum, int x)
{
StackNum->top++;
StackNum->datas[StackNum->top] = x;
}
//运算符进栈
void PushOperateSymbol(OperateSymbol *StackSymbol, char ch)
{
StackSymbol->top++;
StackSymbol->symbol[StackSymbol->top] = ch;
}
//运算数退栈
int PopOperateNum(OperateNum *StackNum)
{
int num;
num = StackNum->datas[StackNum->top];
StackNum->top--;
return num;
}
//运算符退栈
char PopOperateSymbol(OperateSymbol *StackSymbol)
{
char ch;
ch = StackSymbol->symbol[StackSymbol->top];
StackSymbol->top--;
return ch;
}
//判断输入的符号是否是四则运算符号
int IsOperateSymbolOrNum(char ch)
{
if(ch == '+' || ch == '-' || ch == '*'|| ch == '/' || ch == ' ') return 1;
else return 0;
}
//判断符号的优先级
char Priority(char inputnum, char ch)
{
switch(inputnum)
{
//加减在同一个优先级上
case '+':
case '-':
{
if(ch == '+' || ch == '-') return '>';
else if(ch == '*' || ch == '/') return '<';
else return '>';
}
break;
//乘除在同一优先级
case '*':
case '/':
{
if(ch == '+' || ch == '-') return '>';
else if(ch == '*' || ch == '/') return '>';
else return '>';
}
break;
case ' ':
{
if(ch == ' ') return '=';
else return '<';
}
break;
}
}
int Calculate(int num1, char ch, int num2)
{
int result;
switch(ch)
{
case '+':
result = num1 + num2;
break;
case '-':
if(num1<num2)
result=-1000;
else
result=num1-num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if(num1%num2==0)
result = num1 / num2;
else
result=-1000;
}
return result;
}
//用于用户输入和计算结果
int MainCalc()
{
//主函数进行计算
OperateNum datas;
OperateSymbol symbol;
int num1, num2, result, num;
char ch, sign;
//初始化顺序栈
datas.top=-1; //操作数栈顶指针
symbol.top=-1; //操作符栈顶指针
//把回车计算的操作符放在栈中
PushOperateSymbol(&symbol, ' ');
ch = getchar();
while((ch != ' ') || (GetOperateSymbol(&symbol) != ' '))
{
if(!IsOperateSymbolOrNum(ch))
{
num = atoi(&ch); //将字符转换为整数
ch = getchar(); //获取输入
while(!IsOperateSymbolOrNum(ch))
{
num = num * 10 + atoi(&ch);
ch = getchar(); //当没有输入回车时,继续获取输入
}
PushOperateNum(&datas, num);
}
else
{
//考虑第一个数是负数的情况
if(ch=='-'&&symbol.top==0&&datas.top==-1)PushOperateNum(&datas, 0);
switch(Priority(GetOperateSymbol(&symbol), ch))
{
//判断优先级后进行计算
case '<':
PushOperateSymbol(&symbol, ch);
ch = getchar();
break;
case '=':
sign = PopOperateSymbol(&symbol);
ch = getchar(); //获取输入
break;
case '>':
sign = PopOperateSymbol(&symbol);
num2 = PopOperateNum(&datas);
num1 = PopOperateNum(&datas);
result = Calculate(num1, sign, num2);
PushOperateNum(&datas, result);
break;
}
}
}
result = GetOperateNum(&datas);
return result;
}
int main(int argc, char *argv[])
{
int result,cj=0,answer,i;
for(i=0;i<10;){
getchar();
printf("请第%d出题:",i+1);
result = MainCalc();
if(result>=0){
i++;
printf("请输入你的答案:");
scanf("%d",&answer);
if(result==answer){
cj+=10;
printf("恭喜您答对了 ");
}
else{
printf("哦,您答错了,正确答案是:%d ",result);
}
}
else{
printf("不符合规则,请重新出题 ");
}
}
printf("您答对了%d题 ",cj/10);
printf("您的总分为%d ",cj);
}
![](https://img2020.cnblogs.com/blog/1946335/202003/1946335-20200328142816162-483882176.png)
C.托管代码
七、实验总结
1.有点累,早上九点一直到现在,中间吃了半个小时的饭,其他时间一直都在弄。
2.收获很多,结对编程相对于一个人编程,出现错误的几率更小。我在编写时张子毓的提醒一直都很致命,因为一般都是我没有注意到的错误,一提醒意味着又要修改了。之前一个编程是,经常会犯一些小错误,但是没有发现,编译后发现错误了,又很难找到。所以结对编程降低了代码的错误率。同时结对编程,队友相互鼓励,在出现错误的时候会更平静的处理。不会崩溃的说:“啊,又错了!”。
3.通过这次实验也了解到托管的用法,对以后也受益很多,比如以后编写的代码、毕业论文等等,都可以托管保存。再也不用担心丢失了,对于我这种丢三落四的人,简直太棒了。