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

  • 相关阅读:
    【Leetcode】23. Merge k Sorted Lists
    【Leetcode】109. Convert Sorted List to Binary Search Tree
    【Leetcode】142.Linked List Cycle II
    【Leetcode】143. Reorder List
    【Leetcode】147. Insertion Sort List
    【Leetcode】86. Partition List
    jenkins 配置安全邮件
    python 发送安全邮件
    phpstorm 同步远程服务器代码
    phpUnit 断言
  • 原文地址:https://www.cnblogs.com/ChanWunsam/p/10018240.html
Copyright © 2011-2022 走看看