题目:
【问题描述】
用visual studio 2022和EasyX,设计一个简单的计算器,可以实现基本算术表达式、常用函数的计算。
【任务点及要求】
(1)能够以交互式的方式输入数学表达式;
(2)能够对标准整数的四则运算表达式的求值,能正确处理正负数(正负
号)、括号(包括单层及多层嵌套的括号),如:
1+2、(1+2)*3、((1+2)*3+4)/5、1+(-2)、(+1-2)*(-3)
(3)能够对标准浮点数四则运算表达式的求值,能正确处理正负数(正负
号)、括号(包括单层及多层嵌套的括号),如:
1.0+2.0、(1.0+2)*3.0、((1+2.0)*3.0+4)/5.0、(+1.0-2)*(-3.0)
(4)能够计算一个标准整数/浮点数范围内的数的平方、平方根,如:
power(2)、power(3.0)、sqrt(2)、sqrt(3.0)
(5)能够计算指定区间的整数的和,如:
sum(1:5)
(6)能够计算一个标准整数范围内的整数的阶乘,如:
5!
(7)方便易用的交互界面。
【说明】
(1)第(7)部分交互界面可以以文本或图形方式实现,如果以图形界面方
式实现+5 分。
(2)自行设计一些非法表达式,进行程序测试,以保证程序的稳定运行。
【提示】
1.可以参考教材关于中缀表达式、后缀表达式、表达式求值的内容,在此基
础上进行扩充。
2.输入一个“表达式”后,应先判断是否为合法的表达式,即是否由合法的
数字符、运算符、数学函数名以合法的顺序组成,如果包含非法字符或顺序错误,
提示相关信息后终止计算或重新输入。
3.对合法“表达式”,首先将其中的数字串(正负号+连续的数字字符)转换
为对应的数值,注意处理整数、浮点数和正负号。
4.正确解释表达式中的运算符或数学函数,实现相应的计算。注意运算符的
优先级和括号的处理。
5.数学函数(平方、平方根等)可以以表达式的方式表示在数学表达式中,
也可以采用热键(按钮)的方式调用。
下面是我用visual studio2022和EasyX写出来的代码,已经实现图像化功能。
#include <stdio.h>
#include <stdlib.h>
#include <graphics.h>
#include <assert.h>
#include <string.h>
#include <easyx.h>
#include <iostream>
#include <math.h>
#include <stack>
using namespace std;
void calculate(stack<char>& Ope, stack<double>& Num); //用来计算加减乘除, 结果放在数字栈顶
typedef struct Button
{
int WinSizeX;
int WinSizeY;
int WinKeyY;
int WinKeyX;
COLORREF color;
char* text;
}BUTTON, * LPBUTTON;
LPBUTTON createButton(int WinSizeX, int WinSizeY, int WinKeyY, int WinKeyX, const char* str) {
LPBUTTON button = (LPBUTTON)malloc(sizeof(BUTTON));//动态申请
assert(button);
button->WinSizeX = WinSizeX;//x
button->WinSizeY = WinSizeY;//y
button->WinKeyY = WinKeyY;//w
button->WinKeyX = WinKeyX;//h
button->color = RGB(189, 211, 255);
int length = strlen(str) + 1;
button->text = (char*)malloc(sizeof(char) * length);
assert(button->text);
strcpy_s(button->text, length, str);
return button;
}
void drawButton(LPBUTTON button) {
setfillcolor(button->color);
fillrectangle(button->WinSizeX, button->WinSizeY, button->WinSizeX + button->WinKeyY, button->WinSizeY + button->WinKeyX);
setbkmode(TRANSPARENT);
settextstyle(85, 0, "楷体");
settextcolor(BLACK);
solidrectangle(0, 0, 600, 200);
line(0, 200, 600, 200);
line(0, 100, 600, 100);
//文字
int textw = textwidth(button->text);
int texth = textheight(button->text);
int xx = button->WinSizeX + (button->WinKeyY - textw) / 2;
int yy = button->WinSizeY + (button->WinKeyX - texth) / 2;
outtextxy(xx, yy, button->text);
}
int inButton(LPBUTTON button, ExMessage m) {
if (m.x>button->WinSizeX&&m.x<=button->WinSizeX+button->WinKeyY&&m.y>button->WinSizeY&&m.y<=button->WinSizeY+button->WinKeyX)
{
button->color = RGB(236, 244, 255);
return 1;
}
button->color = RGB(189, 211, 255);
return 0;
}
int count = 0;
int main() {
stack<char> Ope;//声明栈,存储操作符
stack<double> Num;//声明栈,存储操作数
char ope_;
double num_;
LPBUTTON CE = createButton(0, 200, 150, 100, "CE");
LPBUTTON factorial1 = createButton(150, 200, 150, 100, "n!");
LPBUTTON daoshu = createButton(300, 200, 150, 100, "1/x");
LPBUTTON exp = createButton(450, 200, 150, 100, "exp");
LPBUTTON zk = createButton(0, 300, 150, 100, "(");
LPBUTTON yk = createButton(150, 300, 150, 100, ")");
LPBUTTON cf = createButton(300, 300, 150, 100, "^");
LPBUTTON chu = createButton(450, 300, 150, 100, "/");
LPBUTTON seven = createButton(0, 400, 150, 100, "7");
LPBUTTON eight = createButton(150, 400, 150, 100, "8");
LPBUTTON nine = createButton(300, 400, 150, 100, "9");
LPBUTTON cheng = createButton(450, 400, 150, 100, "*");
LPBUTTON four = createButton(0, 500, 150, 100, "4");
LPBUTTON five = createButton(150, 500, 150, 100, "5");
LPBUTTON six = createButton(300, 500, 150, 100, "6");
LPBUTTON jian = createButton(450, 500, 150, 100, "-");
LPBUTTON one = createButton(0, 600, 150, 100, "1");
LPBUTTON two = createButton(150, 600, 150, 100, "2");
LPBUTTON three = createButton(300, 600, 150, 100, "3");
LPBUTTON jia = createButton(450, 600, 150, 100, "+");
LPBUTTON zero = createButton(0, 700, 150, 100, "0");
LPBUTTON dian = createButton(150, 700, 150, 100, ".");
LPBUTTON backspace = createButton(300, 700, 150, 100, "<-");
LPBUTTON equal = createButton(450, 700, 150, 100, "=");
LPBUTTON lastClickedButton = nullptr;
HWND handel=initgraph(600, 800);
SetWindowText(handel, "简易计算器");
BeginBatchDraw();
int i = 0;
bool exitLoop = FALSE;
while (!exitLoop)
{
ExMessage m;//获取鼠标消息
peekmessage(&m, EM_MOUSE);
drawButton(CE);
drawButton(factorial1);
drawButton(daoshu);
drawButton(exp);
drawButton(zk);
drawButton(yk);
drawButton(cf);
drawButton(chu);
drawButton(seven);
drawButton(eight);
drawButton(nine);
drawButton(cheng);
drawButton(four);
drawButton(five);
drawButton(six);
drawButton(jian);
drawButton(one);
drawButton(two);
drawButton(three);
drawButton(jia);
drawButton(zero);
drawButton(dian);
drawButton(backspace);
drawButton(equal);
bool buttonClicked = false;
for (int i = 0; i < 600; i += 50)
{
if (inButton(one, m) && m.message == WM_LBUTTONDOWN)
{
TCHAR num[] = _T("1");
outtextxy(i, 20, num);
Sleep(1000);
FlushBatchDraw();
double num_ = 1.0;
Num.push(num_);
lastClickedButton = one;
buttonClicked = true;
}
if (buttonClicked && inButton(two, m) && m.message == WM_LBUTTONDOWN)
{
TCHAR num[] = _T("2");
outtextxy(i, 20, num);
Sleep(1000);
FlushBatchDraw();
double num_ = 2.0;
Num.push(num_);
i += 50;// 输出另一按钮对应的值的代码
buttonClicked = false; // 重置布尔变量,等待下次点击
}
FlushBatchDraw();
}
}
EndBatchDraw();
closegraph();
return 0;
}
void calculate(stack<char>& Ope, stack<double>& Num) {
double a, b;
if (Ope.top() == '-') {
a = Num.top(); Num.pop();
Ope.pop(); //取出负号
if (!Num.empty()) {
if (Ope.empty() || Num.size() == Ope.size())
Ope.push('+'); //如果前面还有数字,就压入+,即变成加负值
}
Num.push(-a); //压入负值
}
}
下面是输入表达式,程序进行中缀转后缀(也可以实现阶乘和乘方等计算),得出正确的运行结果的代码:
#undef UNICODE
#undef _UNICODE
#include <iostream>
#include <easyx.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<stack>
#include <conio.h>
#include <graphics.h>
#include <time.h>
#include<windows.h>
#include <assert.h>
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
CONSOLE_FONT_INFO consoleCurrentFont;//执行鼠标操作必备头文件
//支持带括号的表达式,支持+-*/、^(指数)、取负、!(阶乘)
#include <iostream>//用于引入输入输出流库,它允许使用标准输入输出功能。
#include <stack>//引入了堆栈数据结构的库,允许使用堆栈相关的操作。
#include <math.h>//引入了数学函数库,可以使用包含在此库中的数学函数。
using namespace std;//它允许在代码中直接使用标准库中的名称,而无需在每个名称前面加上命名空间的前缀
double inStack(); //核心函数,将操作符有序的入栈计算, 最后返回结果
void calculate(stack<char>& Ope, stack<double>& Num); //用来计算加减乘除, 结果放在数字栈顶
void factorial(stack<double>& Num); //用来计算阶乘
int priority(char ope_); //用来计算操作符的优先级
int main() {
initgraph(600,800);
double result; //最后的结果
DrawWindow();
DrawTe();
cout << "请输入:\n";
result = inStack(); //将缓冲区的操作符和数字压入栈
cout << "结果是:\n" << result;
return 0;
}
double inStack() {
stack<char> Ope;//声明栈,存储操作符
stack<double> Num;//声明栈,存储操作数
char ope_;
double num_;
while (1) {
if (cin.peek() >= '0' && cin.peek() <= '9') { //判断下一个是否是数字,cin.peek()用于查看输入流中的下一个字符,但不会将其从输入流中移除。它可以帮助我们在不影响后续输入操作的情况下,预先判断输入流中的字符。
cin >> num_;//使用 cin >> num; 时,它会等待用户在标准输入中输入一个值,并将该值存储到变量 num 中。输入的数据类型必须与变量 num 的类型匹配,否则可能会发生错误或产生意外结果。
Num.push(num_); //数字直接入栈。Num.push(num_) 是使用在数组Num的末尾添加一个元素的操作。
}
else {
cin >> ope_;
if (ope_ == '=') {
while (!Ope.empty()) calculate(Ope, Num); //如果符号栈不空,就一直计算
return Num.top(); //如果是等号且符号栈顶为空,就返回数字栈顶元素
}
else if (ope_ == '!') factorial(Num); //如果是!就调用factorial(Num)计算阶乘
else if (ope_ == '(' || Ope.empty()) Ope.push(ope_); //如果符号是左括号或符号栈为空,直接将该符号压入栈
else if (ope_ == ')') { //如果是右括号
while (Ope.top() != '(') calculate(Ope, Num); //一直计算完括号里的
Ope.pop(); //左括号出栈
}
else if (priority(Ope.top()) >= priority(ope_)) { //如果栈顶符号的优先级大于等于当前
while (Ope.top() != '(') {
calculate(Ope, Num); //计算结果压入数字栈,取出当前栈顶
if (Ope.empty() || priority(Ope.top()) < priority(ope_)) break; //当符号栈为空或者待压入的符号优先级高就跳出
}//当遇到一个新的操作符 ope_时,程序会比较栈顶操作符 Ope.top() 的优先级和 ope_ 的优先级。如果栈顶操作符的优先级大于等于 ope_,则会:
//从数字栈中取出两个操作数进行计算,并将结果压入数字栈中。
//如果符号栈为空或者待压入的操作符 ope_ 的优先级高于栈顶操作符的优先级,则跳出循环。
Ope.push(ope_); //压入当前符号
}
else Ope.push(ope_); //否则就压入符号栈
}
}
}
void calculate(stack<char>& Ope, stack<double>& Num) {
double a, b;
if (Ope.top() == '-') {
a = Num.top(); Num.pop();
Ope.pop(); //取出负号
if (!Num.empty()) {
if (Ope.empty() || Num.size() == Ope.size())
Ope.push('+'); //如果前面还有数字,就压入+,即变成加负值
}
Num.push(-a); //压入负值
}
else {
a = Num.top(); Num.pop();
b = Num.top(); Num.pop();
if (Ope.top() == '+') Num.push(b + a);
else if (Ope.top() == '*') Num.push(b * a);
else if (Ope.top() == '/') Num.push(b / a);
else if (Ope.top() == '^') Num.push(pow(b, a));
Ope.pop();
}
}
void factorial(stack<double>& Num) {
int a = static_cast<int>(Num.top()); Num.pop();
int result = 1;
for (int i = 1; i <= a; i++) result *= i;
Num.push(static_cast<double>(result));
}
int priority(char ope_) {
if (ope_ == '(') return 0;
else if (ope_ == '+' || ope_ == '-') return 1;
else if (ope_ == '*' || ope_ == '/') return 2;
else if (ope_ == '^') return 3;
}
目前碰到的问题如下:
1.点击按钮后,无限循环输出这一按钮对应的字符,无法做到只输出一次,等待下一次点击按钮,再输出相应的字符;
2.大致思路是把这两部分代码合并起来,实现完整的计算器功能,即把第二部分的代码(进栈、出栈和中缀转后缀等功能)写入第一部分代码的鼠标事件中的按钮部分。第一部分代码的按钮1中写进了这部分代码,但由于输出遇到问题,不知道这样进栈是否可行。同时,由于输出为字符类型,是否还需具体的类型转换(字符型转换成数值型)?
求助各位大佬,请问代码如何修改可以实现点击按钮后正确输出并且可以将鼠标事件和中缀转后缀结合在同一个程序中呢?