• 数据结构(c++)(3)--简单的计算器


            接着上一篇博客(点击打开链接)中关于栈在中缀表达式和后缀表达式中的应用,这次分享下自己的一个简单的计算器实现的代码。

            那就暴力简单些,直接上代码:

    类定义的代码如下:

    #ifndef CALCULATOR_H
    #define CALCULATOR_H
    #include<string>
    #include<vector>
    
    using namespace std;
    class Calculator
    {
    public:
    	Calculator(int num = 0)
    	{
    		init(num);
    	}
    	bool isValid(const string &expression) const;
    	double calculate(const string &expression) const;    //主要是对后缀表达式求解结果
    	double arithmeticSign(char sign, double num1, double num2) const;   //用于计算两个数之间的结果  
    	friend double string_to_digit(const string &str);  //将字符串转化为数字
    private:
    	int m_num;
    	vector<char> m_standard;
    	void init(int num);
    	bool isStandard(const string &expression) const;
    	bool isSign(char ch) const;      //判断一个字符是否是运算符
    	bool ComparePriority(char sign1, char sign2)const;    //比较两个运算符的优先级
    	void infixToSuffix(const string &expression, vector<double> &num_vec, vector<char> &cmp_vec, vector<bool> &loc_vec)const;   //将一个中缀表达式转化为后缀表达式
    };
    #endif
    
    类的具体实现如下:

    #include<stack>
    #include<iostream>
    #include<map>
    #include"Calculator.h"
    
    using namespace std;
    void Calculator::init(int num)
    {
    	char str[18] = { '0','1','2','3','4','5','6','7','8','9','.','(',')','+','-','*','/' };
    	m_num = num;
    	for (int i = 0; str[i] != ''; i++)
    		m_standard.push_back(str[i]);
    }
    bool Calculator::isStandard(const string &expression) const
    {
    	for (int i = 0; i<expression.size(); i++)
    	{
    		if (find(m_standard.begin(), m_standard.end(), expression[i]) == m_standard.end())
    			return false;
    	}
    	return true;
    }
    bool Calculator::isSign(char ch) const
    {
    	if (find(m_standard.begin() + 11, m_standard.end(), ch) == m_standard.end())
    		return false;
    	return true;
    }
    void Calculator::infixToSuffix(const string &expression, vector<double> &num_vec, vector<char> &cmp_vec, vector<bool> &loc_vec)const
    {
    	if (!isStandard(expression))
    	{
    		cerr << "**********输入有误**********" << endl;
    		exit(EXIT_FAILURE);
    	}
    	if (!isValid(expression))
    	{
    		cerr << "**********输入有误**********" << endl;
    		exit(EXIT_FAILURE);
    	}
    	int first = 0;
    	stack<char> stackChar;
    	for (int i = 0; i<expression.size(); i++)
    	{
    		if (isSign(expression[i]) || (i == expression.size() - 1))
    		{
    			string substr(expression.begin() + first, expression.begin() + i);
    			if (i == expression.size() - 1 && !isSign(expression[i]))   //当i代表最后一个字符且不是运算符的时候,那么这个数字得合并向前一个数字中
    				substr += expression[i];
    			if (!substr.empty())   //此时处理的是数字
    			{
    				double num = string_to_digit(substr);
    				//将数字放入数字向量,并记录相应的位置属性
    				num_vec.push_back(num);
    				loc_vec.push_back(false);
    			}
    			if(isSign(expression[i]))    //处理字符的时候
    			{
    				if (expression[i] == ')')
    				{
    					while (!stackChar.empty())
    					{
    						char cmp = stackChar.top();
    						stackChar.pop();
    						if (cmp == '(')
    							break;
    						//将运算符放入字符向量中,并用位置向量记录位置属性
    						cmp_vec.push_back(cmp);
    						loc_vec.push_back(true);
    
    					}
    				}
    				else
    				{
    					if (!stackChar.empty())
    					{
    						char ch = stackChar.top();
    						if (ch=='('||!ComparePriority(ch, expression[i]))
    						{
    							stackChar.push(expression[i]);
    						}
    						else
    						{
    							cmp_vec.push_back(ch);
    							stackChar.pop();
    							loc_vec.push_back(true);
    						}
    					}
    					else {
    						stackChar.push(expression[i]);
    					}
    				}
    			}
    			first = i + 1;
    		}
    	}
    	while (!stackChar.empty())
    	{
    		cmp_vec.push_back(stackChar.top());
    		stackChar.pop();
    		loc_vec.push_back(true);
    	}
    }
    
    
    bool Calculator::isValid(const string &expression) const
    {
    	if (isStandard(expression) == false)
    		return false;
    	stack<char> stk;
    	for (int i = 0; i<expression.size(); i++)
    	{
    		if (expression[i] == '(' || expression[i] == '[')
    			stk.push(expression[i]);
    		else if (expression[i] == ')')
    		{
    			if (stk.top() != '(')
    				return false;
    			else
    				stk.pop();
    		}
    		else if (expression[i] == ']')
    		{
    			if (stk.top() != '[')
    				return false;
    			else
    				stk.pop();
    		}
    	}
    	if (stk.empty())
    		return true;
    	else
    		return false;
    }
    double Calculator::calculate(const string &expression) const
    {
    	vector<double> num_vec;
    	vector<char> cmp_vec;
    	vector<bool> loc_vec;
    	stack<double> stackDouble;
    	stack<char> stackChar;
    	int index_num = 0, index_cmp = 0;
    
    	//将中缀表达式转化为后缀表达式
    	infixToSuffix(expression, num_vec, cmp_vec, loc_vec);
    
    	for (int i = 0; i < loc_vec.size(); i++)
    	{
    		if (!loc_vec[i])      //当i位置处是数字的时候
    		{
    			stackDouble.push(num_vec[index_num++]);
    		}
    		else
    		{
    			if (stackDouble.size() < 2)
    			{
    				cerr << "对不起,输入的表达式有误,无法计算" << endl;
    				exit(EXIT_FAILURE);
    			}
    			double num1 = stackDouble.top();
    			stackDouble.pop();
    			double num2 = stackDouble.top();
    			stackDouble.pop();
    			stackDouble.push(arithmeticSign(cmp_vec[index_cmp++], num2, num1));
    		}
    	}
    	if (stackDouble.size() != 1 || !stackChar.empty())
    	{
    		cerr << "对不起,输入的表达式有误,无法计算" << endl;
    		exit(EXIT_FAILURE);
    	}
    	return stackDouble.top();
    
    
    }
    
    double string_to_digit(const string &str)
    {
    	double digit = 0;
    	auto it = find(str.begin(), str.end(), '.');
    	if (it == str.end())
    	{
    		int n = 1;
    		for (int i = str.size() - 1; i >= 0; i--)
    		{
    			digit += (str[i] - '0')*n;
    			n *= 10;
    		}
    	}
    	else
    	{
    		int n = 1;
    		int location = it - str.begin();
    		for (int i = location - 1; i >= 0; i--)
    		{
    			digit += (str[i] - '0')*n;
    			n *= 10;
    		}
    		double m = 0.1;
    		for (int i = location + 1; i<str.size(); i++)
    		{
    			digit += (str[i] - '0')*m;
    			m *= 0.1;
    		}
    	}
    	return digit;
    }
    double Calculator::arithmeticSign(char sign, double num1, double num2) const
    {
    	if (sign == '+')
    		return num1 + num2;
    	else if (sign == '-')
    		return num1 - num2;
    	else if (sign == '*')
    		return num1*num2;
    	else if (sign == '/')
    	{
    		if (num2 == 0)
    		{
    			cerr << "********除数不能为0*******" << endl;
    			exit(EXIT_FAILURE);
    		}
    		return num1 / num2;
    	}
    }
    
    bool Calculator::ComparePriority(char sign1, char sign2)const     //用于比较第一个符号的优先级是否比第二个大
    {
    	map<char, int> SignGather;
    	SignGather.insert(pair<char, int>('(', 1));
    	SignGather.insert(pair<char, int>('*', 2));
    	SignGather.insert(pair<char, int>('/', 2));
    	SignGather.insert(pair<char, int>('+', 3));
    	SignGather.insert(pair<char, int>('-', 3));
    	if (SignGather[sign1]<SignGather[sign2])
    		return true;
    	else
    		return false;
    
    }
    
    
    测试的main函数如下:

    #include<iostream>
    #include"Calculator.h"
    using namespace std;
    
    int main()
    {
    	Calculator calculator;
    	string str;
    	cout << "请输入一个数学表达式(其中要求符号是英文,且无空白符):" << endl;
    	cin >> str;
    	double result = calculator.calculate(str);
    	cout << result << endl;
    	return 0;
    
    }
    
    下面主要说一下这个代码的思路:

    (1)首先我们主要是构造一个计算器的类Calculator,用于处理我们的计算表达式。

    (2)对于输入的数学表达式,我们用string进行存放,但是并不会将它作为类的成员变量,因为我们都知道,对于一个计算器而言,没有必要存储这种变量,因为用户会一直输入不同的表达式,所以存储它是没有什么太大的作用的,所以我们这里就不存储它了。

    (3)对于输入的表达式,我们有专门的函数isValid和isStandard对其进行初步的简单检测,以判定是否符合标准。当然对于这个标准而言,我们所定义的标准是存放在成员变量m_standard中的,在这个向量中,我们定义了数学表达式中允许出现的合法的字符。

    (4)之后,就是需要对表达式进行解析了,因为我们是将表达式当做一个string读入的,所以如何判定数字和运算符需要我们自己处理。这里我们定义了一个函数几个函数进行这方面的处理,其中string_to_digit函数会将制定的字符串转化为数字,isSign函数会判断一个字符是否为元素符。在这里我们需要着重注意一下如何拆分表达式,在程序中,对于表达式的处理,我是这样做的:我会从下标0开始逐个读取string表达式中的字符,定义一个first用于标记读的起始位置,当读到运算符的时候我会停下来,因为在数学表达式中,运算符之间的就是操作数了(当然对于“(”、“)”与其它操作符相邻的情况也可以处理的),这个时候,在first和运算符之间的这部分子串就试数字,这个时候我们可以使用函数将数字解析出来,同时也就解析出来运算符。

    (5)对于解析出来的数字和运算符,我们首先需要将它们处理成后缀表达式,这才是我们的主要目的。那么问题来了,这个后缀表达式该如何进行存放呢?继续存放成一个string?这显然不是我们愿意看见的,因为我们好不容易将string解析出来了,这个时候又把它转化回去,想想都不开心。有什么办法可以同时记录下数字和字符的后缀表达式呢?这里呢,我们用三个向量进行存放,分别为num_vec,cmp_vec和loc_vec,分别用于存放数字、运算符和位置参数。在处理string表达式过程中,当解析出一个数字的时候,我们就将它放入num_vec中,并将一个false放入loc_vec中;当解析出一个运算符的时候,我们就将它放入cmp_vec中,并将一个true放入loc_vec中,直至处理完毕。在这里main,loc_vec是很重要的,我们将用它记录数字和运算符的位置,用false表示此处的东西在num_vec中,用true表示此处的东西在cmp_vec中,大家仔细体会下这个处理方法,并结合代码看下。

    (6)在步骤(5)中,我们可以得到一个后缀表达式了,之后便可以根据得到的num_vec,cmp_vec和loc_vec对后缀表达式进行读取和处理,这部分的处理就比较简单了,大家可以看下代码。

            至此,我们的这个简单的计算器就可以使用了,当然啦这是一个命令行的计算器,比较粗糙,后面将会更新一个图形化界面的计算器。当然,这个代码没有经过很多的测试,可能存在不足,如果有错误请大家留言,我会做出相应的修改,这次就分享到这了。





  • 相关阅读:
    IEEE 802.11p (WAVE,Wireless Access in the Vehicular Environment)
    齐夫定律, Zipf's law,Zipfian distribution
    信息中心网络 ,Information-centric networking, ICN
    Ubuntu 16.04安装QQ国际版图文详细教程
    IP多媒体子系统(IP Multimedia Subsystem,IMS)
    遗传算法
    再见, 软交换!又一个通信时代的落幕
    矩阵的核、特征向量、值域
    IPv4组播通信原理
    APIPA(Automatic Private IP Addressing,自动专用IP寻址)
  • 原文地址:https://www.cnblogs.com/hliu17/p/7399973.html
Copyright © 2020-2023  润新知