zoukankan      html  css  js  c++  java
  • 项目——简易计算器

    2017.12.21

    决定做一个小项目来练练手了,对期末考试感到无所畏惧。

    先选择简易计算器吧,核心算法中缀转后缀表达式我还是学过的,最起码能克服一点心里畏惧。

    项目预期如下:

    1. 实现命令行版本的核心算法,做简单的加减乘除就可以了;
    2. 实现图形化窗口。

    代码预期要200行以上.

    对自己提一个要求:可以上网找思路,但是绝对不看别人的源码。


    2017.12.23

    经过几天的努力,总算把这个计算器的核心代码写出来了。代码接近200行,主要利用二重字符串来实现核心的逆波兰算法,实在是有点繁杂。

    这段代码实在不堪一看,各种结构体、变量、函数的命名随心所欲,特别是我在操作栈的时候没有专门写几个函数来执行,因为实在不好分类,显得有点繁杂。这也说明了我这种思路局限性太大,代码复用率不高。

    但毕竟是自己做出来的,发上来见证一下。编辑器用的是VS2017,有些函数会有些奇怪。另外,结果是浮点型,要保证在6位数以内才绝对正确,不然会有取舍。

    // 简易计算器.cpp: 定义控制台应用程序的入口点。
    // 2017.12.23
    
    #include "stdafx.h"
    #include <iostream>
    #include <cstdio>
    #include <conio.h>
    #include <cstdlib>
    using namespace std;
    
    #define MaxSize 100
    
    // 定义数据栈,储存转化为后缀表达式的字符串算式
    typedef struct Data_SNode {
    	int Top;
    	int Ptr;
    	char Elem[MaxSize][MaxSize];
    } *D_Stack;
    
    // 定义操作符栈,用于执行中缀转后缀表达式
    typedef struct Operation_SNode {
    	int Top;
    	char Elem[MaxSize];
    } *Oper_Stack;
    
    // 定义算式栈,用于执行后缀表达式的计算
    typedef struct Counter_SNode {
    	int Top;
    	double Elem[MaxSize];
    } *C_Stack;
    
    D_Stack initData();		// 初始化数据栈
    Oper_Stack initOper();	// 初始化操作符栈
    C_Stack initCounter();  // 初始化算式栈
    void getInput(D_Stack Data, Oper_Stack Oper);				// 接受输入算式并储存在数据栈中
    void PreToSuf(char ch, Oper_Stack Oper, D_Stack Data);		// 操作符中缀转后缀表达式算法,即逆波兰核心算法
    void Count(D_Stack Data, C_Stack Counter);					// 计算后缀表达式
    
    
    int main()
    {
    	D_Stack Data = initData();
    	Oper_Stack Oper = initOper();
    	C_Stack Counter = initCounter();
    	getInput(Data, Oper);
    	Count(Data, Counter);
    
    	return 0;
    }
    
    D_Stack initData()
    {
    	int i, j;
    	D_Stack Data;
    
    	Data = (struct Data_SNode *)malloc(sizeof(struct Data_SNode));
    	for (i = 0; i < MaxSize; i++)
    	{
    		for (j = 0; j < MaxSize; j++)
    		{
    			Data->Elem[i][j] = '';
    		}
    	}
    	Data->Top = -1;
    	Data->Ptr = -1;
    
    	return Data;
    }
    
    Oper_Stack initOper()
    {
    	int i;
    	Oper_Stack Oper;
    	Oper = (struct Operation_SNode *)malloc(sizeof(struct Operation_SNode));
    	for (i = 0; i < MaxSize; i++)
    	{
    		Oper->Elem[i] = '';
    	}
    	Oper->Top = -1;
    
    	return Oper;
    }
    
    C_Stack initCounter()
    {
    	int i;
    	C_Stack Counter;
    	Counter = (struct Counter_SNode *)malloc(sizeof(struct Counter_SNode));
    	for (i = 0; i < MaxSize; i++)
    	{
    		Counter->Elem[i] = 65535;
    	}
    	Counter->Top = -1;
    
    	return Counter;
    }
    
    void getInput(D_Stack Data, Oper_Stack Oper)
    {
    	int flag;			// 标记之前的输入是什么
    	char ch;			// 接收输入字符
    
    	flag = 0;			// flag:0是初始状态,1代表上一次输入数字,2代表上一次输入符号
    	ch = _getche();
    	while (ch != 61)	// 在输入‘=’之前
    	{
    		if (ch >= 48 && ch <= 57)	// 如果输入字符是数字,直接存入
    		{
    			if( flag == 0)		// 初始字符
    			{
    				Data->Elem[++Data->Top][++Data->Ptr] = ch;
    				flag = 1;
    			}
    			else if (flag == 1)	// 前一个输入为数字,继续向后排
    			{
    				Data->Elem[Data->Top][++Data->Ptr] = ch;
    			}
    			else  // flag == 2	// 前一个输入为操作符,换一行排
    			{
    				Data->Ptr = -1;
    				Data->Elem[++Data->Top][++Data->Ptr] = ch;
    				flag = 1;
    			}
    		}
    		else if (ch == 42 || ch == 43 || ch == 45 || ch == 47)	// 如果接受字符是符号,执行中缀转后缀算法
    		{
    			PreToSuf(ch, Oper, Data);
    			flag = 2;
    		}
    		ch = _getche();
    	}
    	while (Oper->Top >= 0)		// 输入‘=’后,将操作符栈中的字符全部取出来
    	{
    		Data->Ptr = -1;
    		Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--];
    	}
    }
    
    void PreToSuf(char ch, Oper_Stack Oper, D_Stack Data)
    {
    	if (Oper->Top == -1)				// 如果栈空
    	{
    		Oper->Elem[++Oper->Top] = ch;
    	}
    	else
    	{
    		if (ch == 42 || ch == 47)		// 如果接受字符为*或/,则将栈顶的*或/全部排出再存入
    		{
    			while (Oper->Elem[Oper->Top] == 42 || Oper->Elem[Oper->Top] == 47)
    			{
    				Data->Ptr = -1;
    				Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--];
    			}
    			Oper->Elem[++Oper->Top] = ch;
    		}
    		else if (ch == 43 || ch == 45)	// 如果接受字符为+或-,则将栈清空再存入
    		{
    			while (Oper->Top >= 0)
    			{
    				Data->Ptr = -1;
    				Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--];
    			}
    			Oper->Elem[++Oper->Top] = ch;
    		}
    	}
    }
    
    void Count(D_Stack Data, C_Stack Counter)
    {
    	int i;
    	double Pre, Later, RST;		// Pre为前数,Later为后数,RST为结果值
    	char Operation;				// 符号
    
    	for (i = 0; i <= Data->Top; i++)
    	{
    		if (Data->Elem[i][0] >= 48)
    		{
    			Counter->Elem[++Counter->Top] = atof(Data->Elem[i]);  // atof()将字符类型转化为double类型
    		}
    		else
    		{
    			Operation = Data->Elem[i][0];
    			Later = Counter->Elem[Counter->Top--];
    			Pre = Counter->Elem[Counter->Top--];
    			switch (Operation)
    			{
    			case 43: RST = Pre + Later; break;		// 加
    			case 45: RST = Pre - Later; break;		// 减
    			case 42: RST = Pre * Later; break;		// 乘
    			case 47: RST = Pre / Later; break;		// 除
    			}
    			Counter->Elem[++Counter->Top] = RST;
    		}
    	}
    
    	cout << Counter->Elem[Counter->Top];
    }
    

    2017.12.24

    本想今天做个界面的,看了一下EasyX的函数觉得好绝望,这要怎么做,做出来估计代码也得重改过了……然后就四处浏览。
    本打算放弃的,毕竟C/C++做图形界面太难了。但是立下的flag怎么能轻易放弃,所以还是继续做吧。今天就是把小数点和括号的功能完善了,这个不是很难。
    改动的就是下面两个核心函数,其它的都照旧,挺水的就过去了。
    另外,给平安夜还在学代码的自己打call,也给大家祝快。

    
    void getInput(D_Stack Data, Oper_Stack Oper)
    {
    	int flag;			// 标记之前的输入是什么
    	char ch;			// 接收输入字符
    
    	flag = 0;			// flag:0是初始状态,1代表上一次输入数字,2代表上一次输入符号
    	ch = _getche();
    	while (ch != 61)	// 在输入‘=’之前
    	{
    		if (ch >= 48 && ch <= 57 || ch == 46)	// 如果输入字符是数字或小数点,直接存入
    		{
    			if( flag == 0)		// 初始字符
    			{
    				Data->Elem[++Data->Top][++Data->Ptr] = ch;
    				flag = 1;
    			}
    			else if (flag == 1)	// 前一个输入为数字,继续向后排
    			{
    				Data->Elem[Data->Top][++Data->Ptr] = ch;
    			}
    			else  // flag == 2	// 前一个输入为操作符,换一行排
    			{
    				Data->Ptr = -1;
    				Data->Elem[++Data->Top][++Data->Ptr] = ch;
    				flag = 1;
    			}
    		}
    		else if (ch == 42 || ch == 43 || ch == 45 || ch == 47 || ch == 40 || ch == 41)	
    		{											// 如果接受字符是符号,执行中缀转后缀算法
    			PreToSuf(ch, Oper, Data);
    			flag = 2;
    		}
    		ch = _getche();
    	}
    	while (Oper->Top >= 0)		// 输入‘=’后,将操作符栈中的字符全部取出来
    	{
    		Data->Ptr = -1;
    		Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--];
    	}
    }
    
    void PreToSuf(char ch, Oper_Stack Oper, D_Stack Data)
    {
    	if (Oper->Top == -1)				// 如果栈空
    	{
    		Oper->Elem[++Oper->Top] = ch;
    	}
    	else
    	{
    		if (ch == 42 || ch == 47)		// 如果接受字符为*或/,则将栈顶的*或/全部排出再存入
    		{
    			while (Oper->Elem[Oper->Top] == 42 || Oper->Elem[Oper->Top] == 47)
    			{
    				Data->Ptr = -1;
    				Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--];
    			}
    			Oper->Elem[++Oper->Top] = ch;
    		}
    		else if (ch == 43 || ch == 45)	// 如果接受字符为+或-
    		{
    			if (Oper->Elem[Oper->Top] == 40)	// 栈顶是(,则直接存入
    				Oper->Elem[++Oper->Top] = ch;
    			else								// 栈顶为其他,则取出至栈顶为(或栈空,再存入
    			{
    				while (Oper->Top >= 0 && Oper->Elem[Oper->Top] != 40)
    				{
    					Data->Ptr = -1;
    					Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--];
    				}
    				Oper->Elem[++Oper->Top] = ch;
    			}
    		}
    		else if (ch == 40)				// 接受字符为(,则直接存入
    		{
    			Oper->Elem[++Oper->Top] = ch;
    		}
    		else if (ch == 41)				// 接受字符为),则将栈内(前字符全部排出,并舍弃(
    		{
    			while (Oper->Elem[Oper->Top] != 40)
    			{
    				Data->Ptr = -1;
    				Data->Elem[++Data->Top][++Data->Ptr] = Oper->Elem[Oper->Top--];
    			}
    			Oper->Top--;
    		}
    	}
    }
    

    2017.12.25

    上网专门找了别人是如何做图形界面的,发现和我想象中的相去甚远。看了下代码,发现要是想实现点击计算的功能,整个代码得重头开始,但其实核心功能我已经实现了。如果只是想输入计算,那么我现在做的已经差不多达到目的了。再做下去就是如何完善细节,已经优化代码的事情了,所以干脆放弃这个小任务。

    想了想还是有点不爽,做了这几天就完成这么一个小玩意,实在没达到目的。既然C/C++图形界面做得这么差劲,实在做不起自己心仪的小项目,那干脆之后学Python吧。

    附上找到的图形界面版本的计算器:
    http://blog.csdn.net/shu_lance/article/details/51570987

  • 相关阅读:
    python 关于文件操作
    python2 编码与解码
    Git系列(二) 冲突解决
    异步回调机制分析
    CSS盒子模型理解
    Git多人协同开发模型
    CSS连载控制背景与CSS精灵
    函数调用在回调,委托与事件在程序设计中的应用
    TFS与Git结合进行代码管理
    Git系列之(二)Git协议与工作协同
  • 原文地址:https://www.cnblogs.com/ChanWunsam/p/10018240.html
Copyright © 2011-2022 走看看