c++生成算式并计算(《构建之法》第一章课后第一题)
c++实现计算器(自动生成算式并计算)
要满足的需求有以下几个:
- 自动生成随机的四则运算算式,包含括号和小数。
- 对生成的算式计算出结果。
- 算式、结果分别存储到不同的文件。
一 生成算式
由上述需求可知,算式中有运算符('+','-','*','/','(',')'),整数,小数(重点是有小数点),同时满足程序模块化设计的要求,所以使用STL中的string类存储生成的算式。
生成算式时有几点需要注意:
- 首先生成一个数字,然后运算符('+','-','*','/')和数字交替出现,可以限定长度为奇数,在偶数位置(位置从0开始)生成数字,奇数位置生成运算符。
- 生成数字有整数有小数。使用一个随机变量控制生成的小数和整数的比例。
- 生成小数时控制好位数、大小。
- 生成括号时注意一个左括号一定要有一个右括号对应,因此设置一个变量存储剩余要生成的右括号的数量,即每生成一个左括号该变量加一,没生成一个右括号该变量减一。同时控制除非到了算式最后,不会出现一对括号只括住一个数字的情况。
在程序中,以上这些由string GenerateExpression()实现,同时它还会调用int GenerateRightBracket(),int GenerateLeftBracket(),char GenerateOperator(),double GeneratNumber(),int GenerateInt()。
二 中缀表达式转为逆波兰式
逆波兰式即后缀表达式,栈可以方便的在计算机中计算后缀表达式的值。
将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:
- 若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈
- 若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级(不包括括号运算符)大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,最后将该运算符送入S1栈。
- 若取出的字符是“(”,则直接送入S1栈顶。
- 若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
- 重复上面的1~4步,直至处理完所有的输入字符
- 若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。
以上来自逆波兰式-百度百科,完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。
在本程序中,使用队列(queue)取代栈S2,省去逆序处理的步骤,这些步骤由函数queue<string> ConvertToRpn(string s,map<string,int>p,map<char,int>p_char)实现。
三 计算逆波兰式
新建一个表达式,如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。
计算功能由double Operation(queue<string> q)实现,同时它还会调用double Calculate(double n1, double n2, char c)。
三 存储到文件
程序运行结果包括单纯的中缀表达式和含结果的中缀表达式,分别存储到不同的文件。两文件分别如下:


完整代码
/*
构建之法第一章习题一,实现自动生成四则运算的算式并判断用户计算的正误。
计划分三步走:
1.自动生成算式
2.输入算式转换为逆波兰式
3.计算算式结果
*/
#include<iostream>
#include<string>
#include<sstream>
#include<stack>
#include<queue>
#include<map>
#include<fstream>
using namespace std;
//将中缀表达式转换为逆波兰式
queue<string> ConvertToRpn(string s,map<string,int>p,map<char,int>p_char)
{
int length = s.length();
string temp_s="";
string temp_for_push;
stack<string>sk1;
queue<string>sk2;
sk1.push("#");
for (int i = 0; i < length;)
{
if (isdigit(s[i]))
{//判断字符是否是0~9的数字
while (isdigit(s[i]) || s[i] == '.')
{
temp_s = temp_s + s[i];
i++;
}
sk2.push(temp_s);
temp_s.clear();
}
else
{
if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/'||s[i]=='^')
{
if (p_char[s[i]] >p[sk1.top()])
{
temp_for_push.clear();
temp_for_push = temp_for_push + s[i];
sk1.push(temp_for_push);
i++;
}
else
{
while (p_char[s[i]] <= p[sk1.top()])
{
sk2.push(sk1.top());
sk1.pop();
}
temp_for_push.clear();
temp_for_push = temp_for_push + s[i];
sk1.push(temp_for_push);
i++;
}
}
else if (s[i] == '(')
{
temp_for_push.clear();
temp_for_push = temp_for_push + s[i];
sk1.push(temp_for_push);
i++;
}
else if(s[i]==')')
{
while (sk1.top() != "(")
{
sk2.push(sk1.top());
sk1.pop();
}
sk1.pop();
i++;
}
}
if (i == length)
{
while (sk1.size() != 1)
{
sk2.push(sk1.top());
sk1.pop();
}
}
}
return sk2;
}
//生成随机小数
double GeneratNumber()
{
double number;
int temp;
number = ((double)rand()) / ((double)(rand()/50));
temp = number * 10;
number = ((double)temp) / 10;
number = number - (int)number + (int)number % 49;
return number;
}
//生成随机整数
int GenerateInt()
{
double int_number;
int_number = rand() % 49;
return int_number;
}
//计算逆波兰式中简单表达式
double Calculate(double n1, double n2, char c){
double result = 0;
if (c == '+'){
result = n1 + n2;
}
else if (c == '-'){
result = n2 - n1;
}
else if (c == '*'){
result = n1*n2;
}
else if (c == '/'){
result = n2 / n1;
}
return result;
}
//计算逆波兰式
double Operation(queue<string> q)
{
stack<double> temp_for_digit;
char temp_for_char;
double temp_for_push = 0;
double num1, num2;
double temp_result = 0;
int length = q.size();
stringstream ss;
while (q.size() != 0)
{
if (isdigit(q.front()[0]))
{
ss << q.front();
ss >> temp_for_push;
temp_for_digit.push(temp_for_push);
q.pop();
ss.clear();
}
else
{
temp_for_char = q.front()[0];
q.pop();
num1 = temp_for_digit.top();
temp_for_digit.pop();
num2 = temp_for_digit.top();
temp_for_digit.pop();
temp_result = Calculate(num1, num2, temp_for_char);
temp_for_digit.push(temp_result);
}
}
return temp_result;
}
//生成随机运算符
char GenerateOperator()
{
char result;
int which = rand() % 6;
if (which == 0 || which == 4)
{
result = '+';
}
else if (which == 1 || which == 5)
{
result = '-';
}
else if (which == 2)
{
result = '*';
}
else if (which == 3)
{
result = '/';
}
return result;
}
//生成左括号
int GenerateLeftBracket()
{
int result = 0;
int whether_bracket = rand() % 7;
if (whether_bracket ==1)
{
result = 1;
}
return result;
}
//生成右括号
int GenerateRightBracket()
{
int result = 0;
int whether_bracket = rand() % 7;
if (whether_bracket <= 5)
{
result = 1;
}
return result;
}
//生成表达式
string GenerateExpression()
{
string expression = "";
string temp_string;
int count_right_bracket = 0;
int length = 3;
int location_for_last_bracket = 0;
length += 2*(rand() % 15);
stringstream ss;
double temp_num;
int whether_int = 0;
int whether_bracket = 0;
for (int i = 0; i < length; i++)
{
whether_int = rand() % 5;
if (i % 2 == 0)
{
if (whether_int <= 3)
{//80%生成整数
temp_num = GenerateInt();
}
else
{
temp_num = GeneratNumber();
}
ss << temp_num;
ss >> temp_string;
expression += temp_string;
ss.clear();
if (count_right_bracket&&i>=location_for_last_bracket+3)
{
if (GenerateRightBracket())
{
count_right_bracket -= 1;
expression += ')';
}
}
}
else
{
expression += GenerateOperator();
whether_bracket= GenerateLeftBracket();
if (whether_bracket == 1)
{
expression += '(';
count_right_bracket += whether_bracket;
location_for_last_bracket = i;
}
}
}
while ((count_right_bracket--) != 0)
{
expression += ')';
}
return expression;
}
int main()
{
map<string, int> priorites;
priorites["+"] = 1;
priorites["-"] = 1;
priorites["*"] = 2;
priorites["/"] = 2;
priorites["^"] = 3;
map<char, int> priorites_char;
priorites_char['+'] = 1;
priorites_char['-'] = 1;
priorites_char['*'] = 2;
priorites_char['/'] = 2;
priorites_char['^'] = 3;
string expression;
queue<string> RPN;
double result;
int count_expression;
ofstream just_expression, answer;
just_expression.open("expression.txt");
answer.open("answer.txt");
cout << "how many expressions do you want: " << endl;
cin >> count_expression;
for (int i = 0; i<count_expression; i++)
{
expression = GenerateExpression();
RPN = ConvertToRpn(expression,priorites,priorites_char);//得到后缀表达式
result = Operation(RPN);
just_expression << i+1 << ". " << expression << endl;
answer << i+1 << ". " << expression << " = " << result << endl;
expression.clear();
RPN = queue<string>();//清空当前队列
}
just_expression.close();
answer.close();
cout << "finished" << endl;
system("pause");
return 0;
}
c++生成算式并计算(《构建之法》第一章课后第一题)的更多相关文章
- <构建之法>3-5章感想
提示:(下面的总结我会按照每章发现的问题,自己的回答,感想来陈述) 3章. 在阅读3.2.4职业成长-自我评估的时候,说到CRUD需要一些核心技术和许多控扩展的知识,那么作为软件工程的学生,在学校除了 ...
- [第二届构建之法论坛] 预培训文档(Java版)
本博客是第二届构建之法论坛暨软件工程培训活动预培训文档中[适用于结对编程部分的Java版本],需要实验者有一部分Java基础. 目录 Part0.背景 Part1.配置环境 配置JDK Linux 平 ...
- [第二届构建之法论坛] 预培训文档(C++版)
本博客是第二届构建之法论坛暨软件工程培训活动预培训文档中[适用于结对编程部分的C++版本],需要实验者有一部分C++基础. 目录 Part0.背景 Part1.配置环境 Part2.克隆项目 Part ...
- 《构建之法》——GitHub和Visual Studio的基础使用
git地址 https://github.com/microwangwei git用户名 microwangwei 学号后五位 62214 博客地址 https://www.cnblogs.com/w ...
- 初窥构建之法——记2020BUAA软工个人博客作业
项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任建) 这个作业的要求在哪里 个人博客作业 我在这个课程的目标是 完成一次完整的软件开发经历并以博客的方式记录开发过程的心得掌握 ...
- 阅读《构建之法》之FAQ
注:本文档已提交Github,地址是这个 欢迎大家通过PR的方式或者在本博客下留言的方式随时补充意见和建议,我们会持续更新 书中7.2.4的表7-1 MSF团队模型和关键质量目标里面提到的" ...
- 《构建之法》教学笔记——Python中的效能分析与几个问题
<构建之法:现代软件工程>中第2章对效能分析进行了介绍,基于的工具是VSTS.由于我教授的学生中只有部分同学选修了C#,若采用书中例子讲解,学生可能理解起来比较困难.不过所有这些学生都学习 ...
- 《构建之法》课程进度之Github、Travis等工具融入篇
<构建之法>里有一个16周的软件工程课程进度设计.本文在该基本设计的基础上,围绕github.com(源码管理).travis-ci.org(持续集成).单元测试工具.日志工具.少数实用U ...
- 20165234 [第二届构建之法论坛] 预培训文档(Java版) 学习总结
[第二届构建之法论坛] 预培训文档(Java版) 学习总结 我通读并学习了此文档,并且动手实践了一遍.以下是我学习过程的记录~ Part1.配置环境 配置JDK 原文中提到了2个容易被混淆的概念 JD ...
随机推荐
- 解决报错:scandir() has been disabled for security reasons
服务器环境: LNMP 在服务器部署代码时候.遇到了这个问题. 蛋疼啊! 2 解决办法: 打开phpinfo.php , 搜索: scandir 找到disabled_function,确认此函数未 ...
- 【Java nio】Blocking nio2
package com.slp.nio; import org.junit.Test; import java.io.File; import java.io.IOException; import ...
- 用CMAKE编译配置的项目进行调试的方法
在Linux 下用CMAKE编译的项目进行Debug 需进行设置: 1.在未设置之前 进行调试可能会出现错误报告:No source available for ...等一系列错误,这些错误可能就是你 ...
- Linux系统下Redis缓存安装配置
Redis是一个高性能的key-value数据库,现时越来越多企业与应用使用Redis作为缓存服务器.楼主是一枚JAVA后端程序员,也算是半个运维工程师了.在Linux服务器上搭建Redis,怎么可以 ...
- poj1821 Fence【队列优化线性DP】
Fence Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 6122 Accepted: 1972 Description ...
- Redundant Paths---poj3177(双连通分量)
题目链接:http://poj.org/problem?id=3177 题意:有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走.现已有m条路,求至少要新建多少条路,使 ...
- Keras-在预训练好网络模型上进行fine-tune
在深度学习的学习过程中,可能会用到一些已经训练好的模型,比如Alex Net,google Net,VGG,Resnet等,那我们怎样对这些训练好的模型进行fine-tune来提高准确率呢? 参考文章 ...
- Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches Slab内存管理机制 SLUB内存管理机制
Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches Slab内存管理机制 SLUB内存管理机制 http://w ...
- HTML基础之DOM常用操作
DOM(Document Object Model ),文档对象模型,主要用于对HTML和XML文档的内容进行操作. 一.查找节点 直接获取标签 document.getElementById('i1 ...
- 经验搜索排名---google已经做过类似的了(我想多了)
由于编程的原因,我们需要经常的查资料,现在转载的文章比较多,我们经常看到的搜索结果的前十名基本上有7名是转载的.这样看起来很没有效率,后来突然想到,如果把大家的浏览结果搜集起来,然后进行权重排名,这样 ...