第2次作业:熟悉使用工具
GIT地址 | Git地址 |
---|---|
GIT用户名 | Misizu-star |
学号后五位 | 61126 |
博客地址 | 我的博客 |
作业链接 | 个人第2次作业:熟悉使用工具 |
Part1.配置环境
1.VS2017
下载:目前VS更新到VS2019,(本人几乎使用过vs各种版本,推荐vs2017)下载VS2017去需要官网的找旧版本。下载地址
安装:运行引导安装程序
等待下载安装,安装后设置一些个人配置,运行如图
2.git
下载:下载地址 根据自己电脑配置选择对应版本
安装:安装过程建议选择默认,若在桌面点鼠标右键出现此标志则安装成功
part2.代码实现
背景
阿超家里的孩子上小学一年级了,这个暑假老师给家长们布置了一个作业:家长每天要给孩子出一些合理的,但要有些难度的四则运算题目,并且家长要对孩子的作业打分记录。作为程序员的阿超形成了这个软件的需求:
- 程序接收一个命令行参数 n,然后随机产生 n 道加减乘除(分别使用符号+-*/来表示)练习题,每个数字在 0 和 100 之间,运算符在 2 个 到 3 个之间。
- 软件所出的练习题在运算过程中不得出现非整数,比如不能出现 3÷5+2=2.6 这样的算式。
- 练习题生成好后,将生成的 n 道练习题及其对应的正确答案输出到一个文件 subject.txt 中。
设计过程
此次作业代码使用C++完成,创建一个名为Calculator的类,类成员包含生成题目,计算题目两个函数。
1.生成题目
- 思路:使用rand()函数产生随机数、操作符个数、操作符下标,存储在类型为string的变量中。代码如下:
string Calculator::MakeQuestion() {
string question = "";
int number1 = random(1, 100);
int op_num = random(1, 3); //随机产生操作符个数
question += to_string(number1);
for (int i = 0; i < op_num; i++) {
int op_index = random(0, 3); //操作符下标()
int number2 = random(1, 100);
if (op_index == 3) {
while (number1 < number2 || number1 % number2 != 0) {
number2 = random(1, 50); //不能整除则重新生成随机数
}
}
number1 = number2;
question += op[op_index] + to_string(number1); //Calculator成员中 string op[4] = { "+","-","*","/" };
}
question += "=";
return question;
}
2.计算题目
- 探索:在百度上了解中缀表达式和后缀表达式,解决方法为:先将表达式转化为后缀表达式,通过使用栈操作计算后缀表达式(后缀表达式真是个好东西)。但中缀表达式转后缀表达式比较麻烦
不会用代码实现。通过与同学讨论找到一个简单方法。
- 思路:所有的四则运算都是先对两个数据进行运算,再将运算结果通下一个数据进行运算,直到所有数据运算完为止。难点就在于运算符的优先级。
- 实现:使用两个栈:数据栈和运算符栈;设置运算符优先级;遍历字符串:
1.遇到数据直接push数据栈
2.遇到运算符分两种情况:
①运算符栈为空 或 栈外运算符优先级 > 栈顶运算符优先级:运算符直接入栈
②栈外运算符优先级 <= 栈顶运算符优先级:将数据栈头两个数据和运算符栈栈顶pop,进行计算并将结果再push进数据栈,直到运算符栈为空,运算符才入栈。
3.最后的数据栈栈顶便是表达式结果。
话不多说,上代码:
int Calculator::Solve(string question) {
stack<int> num_stack; //保存运算数据的栈
stack<char> op_stack; //保存操作符的栈
int i = 0;
while (i < question.length()) {
char temp = question[i];
if (temp != '+'&&temp != '-'&&temp != '*'&&temp != '/'&&temp != '=') {
int number = stoi(question.substr(i)); //取出完整运算数据
num_stack.push(number); //运算数据直接入栈
i += to_string(number).length();
}
else {
if (op_stack.empty())
op_stack.push(temp);
else if (Prec(temp) > Prec(op_stack.top()))
op_stack.push(temp); //栈外操作符优先级大于栈顶
else { //栈外操作符优先级小于或等于栈顶
while (!op_stack.empty()) {
char op = op_stack.top();
int num2 = num_stack.top();
num_stack.pop();
int num1 = num_stack.top();
num_stack.pop();
if (op == '+')
num_stack.push(num1 + num2);
else if (op == '-')
num_stack.push(num1 - num2);
else if (op == '*')
num_stack.push(num1 * num2);
else if (op == '/')
num_stack.push(num1 / num2);
op_stack.pop();
}
op_stack.push(temp);
}
i++;
}
}
return num_stack.top();
}
3、写入文件
将数据写入文件比较简单,参考博客。效果如下:
part3.单元测试与调试
- 单元测试:啥?还要单元测试,单元测试啥玩意儿?em......在可爱的百度的帮助下找到 这篇博客 。里面介绍了单元测试的基本步骤和注意事项。通过几番倒腾终于掌握了单元测试的基础方法。附图如下:
在单元测试运行完毕后,VS 会弹出一个测试结果窗口。绿色代表通过,红色代表失败。
测试应考虑多种情况,多次测试,防止隐藏Bug。
- 调试:调试较为简单,调试(F5)、断点(F9)、逐过程调试(F10)、逐语句调试(F11)。
1、通过二分法设置断点调试(F5)找到bug的大致位置。
2、逐语句调试,并在监视栏里添加变量函数,与预想结果比较,找出逻辑错误。
part4.回归测试
回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。也就是说修改代码后再次进行单元测试。
我的程序规模较小,单元测试也较为简单,在回归测试中也没有遇到什么问题。
part5.Git提交
参考这篇博客的Git提交代码步骤及常见错误和解决方法。
通过实际操作总结出简单几步:(前三步)
- 1.git配置
git config --global user.name "YourName"
git config --global user.email YourEmail- 2.克隆到本地:
git clone url "url"为代码地址- 3.提交文件:
git add 将文件提交到缓存区
git status 检查状态,若提交到暂存区则会显示提交的所有文件
git commit -m "输入描述" (描述自己写)
git push 将缓存区所有文件提交到github- 4.pull request
在自己的GitHub上点击 “New pull request”
再点击发起请求,审核通过则是以下效果:
由于提交后原仓库已经改变,前三步操作没能截到图,emm......
part6.个人感想
- 收获:通过本次作业,现在我对Git和Github基本熟悉,基本掌握了VS2017的单元测试方法,了解到了断言(Assert)的用处,对调试也更加熟练。
在代码实现过程中使用到 stoi()(将字符串转化为int类型) 和substr()(取出字符串的子串)将两个结合使用:
string str="abc123cd";
int num=stoi(str.substr(4));
cout << num << endl;
得到的结果是23,str.substr(4)得到的子串为"23cd",再用stoi()转化后就只剩23了,(不懂,对stoi()函数了解不深)但这样却能将字符串中未知长度的数字提取出来。
- 总之,动手实践。不懂的就多百度,看看别人遇到此类问题是怎么解决的,多看多学。另外,英语和专业书籍真的很重要😢。