lang:计算器

23 篇文章 0 订阅

前言

目前这个计算器还存在的问题:数字只能是单个整数、只能进行加减乘除、取余、乘方的操作!
计算器是什么?输入2+3X1,会得到5的结果。这也是在做字符串识别,同样涉及编译原理的知识。先算2+3,还是先算3X1,这需要判断优先级!


自定义栈

自定义的栈要求就是能放入和弹出自定义对象!用STL的容器也行!关键在于知道什么时候该释放内存,在哪释放内存,释放哪里的内存!

下面这个自定义栈使用的企业链表的方式构建的,每一个节点只有一个next指针,所以在站内部是访问不到对象的其他元素的!!!
企业链表就像挂衣服一样!自定义一个对象,其中包含一个LinkNode节点,在传递进来时,把整个对象类型转换成LinkNode*,拿出来时再转换成自定义的数据对象就行!在这个过程中,实例对象除了LinkNode的next节点,其他数据都被隐藏起来了。

//LinkStack.h
#pragma once


#include <iostream>
#include <string>
#include <cmath>

//用链表来实现栈

//节点
typedef struct LINKNODE {
	struct LINKNODE* next;
}LinkNode;
//栈
typedef struct LINKSTACK {
	LinkNode head;//头节点
	int size;
}LinkStack;



//初始化
LinkStack* Init_LinkStack();
//入栈
void Push_LinkStack(LinkStack* stack, LinkNode* data);
//返回栈顶元素
LinkNode* Top_LinkStack(LinkStack* stack);
//出栈
void Pop_LinkStack(LinkStack* stack);
//返回栈元素个数
int Size_LinkStack(LinkStack* stack);
//是否为空
bool IsEmpty(LinkStack* stack);
//清空栈
void Clear_LinkStack(LinkStack* stack);
//销毁栈
void FreeSpace_LinkStack(LinkStack* stack);



//LinkStack.cpp
#include "LinkStack.h"




//初始化
LinkStack* Init_LinkStack() {

	//LinkStack* stack = (LinkStack*)malloc(sizeof(LinkStack));
	LinkStack* stack = new LinkStack;
	stack->head.next = nullptr;//没有后续节点
	stack->size = 0;//大小为0
	return stack;
}
//入栈
void Push_LinkStack(LinkStack* stack, LinkNode* data) {

	if (stack == nullptr||data==nullptr) {
		return;
	}
	//把data节点接到头节点后面
	data->next = stack->head.next;
	stack->head.next = data;
	stack->size++;

}
//返回栈顶元素
LinkNode* Top_LinkStack(LinkStack* stack) {
	if (stack == nullptr || stack->size == 0) {
		return nullptr;
	}
	return stack->head.next;//第一个有效节点
}
//出栈
void Pop_LinkStack(LinkStack* stack) {
	if (stack == nullptr||stack->size==0) {
		return;
	}
	//第一个有效节点
	LinkNode* pNext = stack->head.next;
	stack->head.next = pNext->next;
	//删除该节点,数据内存在链表外面,所以内部操作不能删除内存
	/*delete pNext;
	pNext = nullptr;*/
	stack->size--;


}
//返回栈元素个数
int Size_LinkStack(LinkStack* stack) {
	if (stack == nullptr) {
		return -1;
	}
	return stack->size;
}
//是否为空
bool IsEmpty(LinkStack* stack) {
	if (stack == nullptr||stack->size==0) {
		return true;
	}
	return false;
}
//清空栈
void Clear_LinkStack(LinkStack* stack) {
	if (stack == nullptr) {
		return;
	}
	//先拿到第一个有效节点
	stack->head.next = nullptr;
	stack->size = 0;

}
//销毁栈
void FreeSpace_LinkStack(LinkStack* stack) {

	if (stack == nullptr) {
		return;
	}
	//能回收的内存只能是stack这一个头节点
	//free(stack);
	stack->head.next = nullptr;
	stack->size = 0;
	delete stack;
	stack = nullptr;

}



自定义数据结构

typedef struct MYCHAR {
	LinkNode node;
	char* p;
}MyChar;

中缀表达式转后缀表达式

中缀表达式就是我们写的计算式子,这种式子计算机没法认,所以需要转换成后缀表达式。像这样,5+4 => 5 4 +
1+2*3 => 1 2 3 * +
8+(3-1)*5 => 8 3 1 - 5 * +
如何得到后缀表达式?
遍历中缀表达式中的数字和符号
1,对于数字,直接输出
2,对于符号,
         \;\;\;\; (1)如果是左括号,左括号进栈
         \;\;\;\; (2)如果是运算符,运算符号与栈顶符号进行优先级比较
   \;    \;              \;\;\;\;\;\; 1> 如果栈顶符号优先级低,此符号进栈
   \;    \;    \;              \;\;\;\;\;\; 2> 如果栈顶符号优先级不低,将栈顶符号弹出并输出,直到栈里没有元素或者栈顶符号                                    \;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\; 优先级低于此符号之后,此符号进栈
         \;\;\;\; (3)如果是右括号,将栈顶符号弹出并输出,直到匹配到左括号
遍历结束后将栈中所有符号弹出并输出

^表示乘方操作、%表示MOD取余操作,因为这两个和加减乘除一样都是二元操作符,所以直接设定一下优先级,直接加入与加减乘除没有区别。

//main.cpp

//创建MyChar
MyChar* CreateMyChar(LinkStack* stack, char* p) {
	MyChar* mychar = new MyChar;
	mychar->p = p;
	return mychar;
}

//判断是否是数字
bool IsNumber(char* p) {
	return *p >= '0'&&*p <= '9';
}
//数字操作
void NumberOperate(char* p) {
	std::cout << *p;
}
//判断是否是左括号
bool IsLeft(char* p) {
	return *p == '(';
}
//左括号操作
void LeftOperate(LinkStack* stack, char* p) {
	Push_LinkStack(stack, (LinkNode*)CreateMyChar(stack, p));
}

//判断是否是右括号
bool IsRight(char *p) {
	return *p == ')';
}

//右括号操作
void RightOperate(LinkStack* stack, char* p) {
	while (Size_LinkStack(stack) > 0) {
		MyChar* mychar = (MyChar*)Top_LinkStack(stack);
		//如果匹配到左括号
		if (IsLeft(mychar->p)) {
			Pop_LinkStack(stack);
			break;
		}
		//输出并弹出
		std::cout << *(mychar->p);
		Pop_LinkStack(stack);
		//释放内存
		delete mychar;
	}
}

//判断运算符
bool IsOperator(char* p) {
	return *p == '+' || *p == '-' || *p == '*' || *p == '/'||*p=='%'||*p=='^';
}

//返回运算符优先级
int GetPriority(char* p) {
	if (*p == '^') {
		return 1;
	}
	else if (*p == '*' || *p == '/'||*p=='%') {
		return 2;
	}
	else if (*p == '+' || *p == '-') {
		return 3;
	}
	else if (*p=='('||*p==')') {
		return 4;
	}
	return 5;
}

//操作符操作
void OpOperate(LinkStack* stack, char* p) {

	if (stack == nullptr || p == nullptr) {
		return;
	}

	//取出栈顶符号
	MyChar* mychar = (MyChar*)Top_LinkStack(stack);
	//如果栈里没有符号,直接入栈
	if (mychar == nullptr) {
		Push_LinkStack(stack, (LinkNode*)CreateMyChar(stack, p));
		return;
	}

	while (Size_LinkStack(stack) > 0) {

		mychar = (MyChar*)Top_LinkStack(stack);

		if (GetPriority(mychar->p) > GetPriority(p)) {	//如果优先级低
			Push_LinkStack(stack, (LinkNode*)CreateMyChar(stack, p));
			return;
		}
		//输出并弹出
		std::cout << *(mychar->p);
		Pop_LinkStack(stack);
		//释放内存
		delete mychar;

	}
	//没有遇到优先级低的,所以只有等到所有元素弹完了后,再把此符号压入栈
	Push_LinkStack(stack, (LinkNode*)CreateMyChar(stack, p));


}


int main(){
		char* str = (char*)"8+(3-1)*5";
		std::cout << "需要计算的式子为:\n"<<str<<std::endl;
		char* p = str;

		//创建栈
		LinkStack* stack = Init_LinkStack();

		while (*p != '\0') {
			if (IsNumber(p)) {
				NumberOperate(p);
			}
			else if (IsLeft(p)) {
				LeftOperate(stack, p);
			}
			else if (IsRight(p)) {
				RightOperate(stack, p);
			}
			else if (IsOperator(p)) {
				OpOperate(stack, p);
			}
			p++;
		}

		//最后把栈里所有符号弹出
		while (Size_LinkStack(stack) > 0) {
			MyChar* mychar = (MyChar*)Top_LinkStack(stack);
			//输出并弹出
			std::cout <<*(mychar->p);
			Pop_LinkStack(stack);
			//释放内存
			delete mychar;
			mychar = nullptr;
		}
		//清空栈
		FreeSpace_LinkStack(stack);
	
	system("pause");
}

后缀表达式求解

后缀表达式里只有运算符和数字!!!
遍历后缀表达式中的运算符和数字
1,对于数字,直接进栈
2,对于符号
       \;\;\; (1)从栈中弹出右操作数
       \;\;\; (2)从栈中弹出左操作数
       \;\;\; (3)根据符号进行运算
       \;\;\; (4)将这个结果再压入栈中
遍历结束,栈中唯一数字为计算结果!

当遇到负数时,负数前面的符号不是二元的,是一元的!但是可以当做是二元的,其左操作数当做是零!!!所以在只有右操作数,没有左操作数时,就是遇到了一元运算符!!!
如果此时符号是减号,那么左操作数补个零就行。
如果此时符号是其他的一元操作符,需要另外考虑。

前面显示出后缀表达式但是没有接收,所以需要用一个string的遍历接收一下!

//main.cpp
//初始化后缀表达式的接收变量
std::string suffix_expr = "";
//在每一个输出字符的地方,换成拼接字符串
suffix_expr += *p;
//suffix_expr += *(mychar->p);
//main.cpp
typedef struct MYNUM {
	LinkNode node;
	double val;
}MyNum;
double Caculate(double leftNum, double rightNum, char* p) {
	double ret = 0;
	switch (*p) {
	case '+'://加
		ret = leftNum + rightNum;
		break;
	case '-'://减
		ret = leftNum - rightNum;
		break;
	case '*'://乘
		ret = leftNum * rightNum;
		break;
	case '%'://取余
		ret = (int)leftNum % (int)rightNum;
		break;
	case '/'://if (rightNum == 0) {
			std::cout << "除数不能为零!!!" << std::endl;
			return -1;
		}
		ret = leftNum / rightNum;
		break;
	case '^'://乘方
		ret = pow(leftNum, rightNum);
		break;
	default:
		std::cout << "计算符号不对!!!" << std::endl;
		return -1;
	}
	return ret;

}


int main(){  

	char ch='y';
	do {

		//初始化字符串
		suffix_expr = "";

		//char* str = (char*)"8+(3-1)*5";
		char str[50];
		std::cout << "需要计算的式子为:\n";
		std::cin >> str; 


	    //.....中间代码省略

	    
		//创建新栈
		LinkStack* stack2 = Init_LinkStack();



		//后缀表达式成功得到了
		char* str2 = const_cast<char *>(suffix_expr.c_str());
		std::cout << "式子的后缀表达式为:\n" << str2 << std::endl;

		p = str2;

		while (*p != '\0') {
			if (IsNumber(p)) {
				//数字直接入栈
				MyNum* mynum = new MyNum;
				mynum->val = *p - '0';//转换成数字
				Push_LinkStack(stack2, (LinkNode*)mynum);
			}
			else if (IsOperator(p)) {
				//先弹出右操作数
				MyNum* right = ((MyNum*)Top_LinkStack(stack2));
				double rightNum = right->val;
				Pop_LinkStack(stack2);
				delete right;
				right = nullptr;

				//取出左操作数
				MyNum* left = ((MyNum*)Top_LinkStack(stack2));
				double leftNum=0;//当栈为空时,即此运算符应该是一元运算符
				if (left ) {//如果栈不为空
					leftNum = left->val;
					Pop_LinkStack(stack2);
					delete left;
					left = nullptr;
				}
				
				

				//根据操作符进行计算
				MyNum* mynum = new MyNum;
				mynum->val = Caculate(leftNum, rightNum, p);
				//把计算结果再压入栈中
				Push_LinkStack(stack2, (LinkNode*)mynum);

			}

			p++;


		}

		if (Size_LinkStack(stack2) == 1) {
			MyNum* mynum = ((MyNum*)Top_LinkStack(stack2));
			std::cout << "计算结果是:\n" << mynum->val << std::endl;
			Pop_LinkStack(stack2);
			delete mynum;
			mynum = nullptr;
		}
		else {
			std::cout << "栈中元素还有 " << Size_LinkStack(stack2)<<" 个"<<std::endl;
		}

		//释放栈
		FreeSpace_LinkStack(stack2);


		std::cout << "继续吗?Y/N" << std::endl;
		std::cin >> ch;
	}while (ch=='y'||ch=='Y');

	system("pause");
}

另外,这个计算器只能算1+2*(7%2-5^2)/9这个样子的式子,遇到三角函数和对数没办法!
下面是实际效果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

念心科道尊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值