项目——简易计算器

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] = ‘\0‘;
        }
    }
    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] = ‘\0‘;
    }
    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

原文地址:https://www.cnblogs.com/ChanWunsam/p/10018240.html

时间: 2024-10-10 17:33:23

项目——简易计算器的相关文章

第一个Android项目--简易计算器的设计与实现

这个简易计算器的实现我是参照慕课网上的视频课程学习的,下面梳理我的开发过程以及DEBUG 在这个项目中实现计算器的第一步骤是对界面UI的设计,UI的设计并不难,一个总的Lnearlayout的布局下orientation设置为vertical垂直分布,然后此布局下再设置1给我Edittext的一个文本框4个Lnearlayout子布局(第4个布局里可以嵌套另外3个Lnearlayout的布局来实现按钮排版)这4个子布局在你的界面上肯定是垂直分布的,因为你的总布局设置vertical.第一个子布局

结对-简易计算器-设计文档

项目:简易计算器 本设计的大概需要的功能:1.可以判断是小数点前还是后 2.需要初始化小数点后的倍率 3.可以标记加减乘除 4.需要记录上一轮结果 5.实现每个数字按钮和算数按钮

制作一个简易计算器——基于Android Studio实现

一个计算器Android程序的源码部分分为主干和细节两部分. 一.主干 1. 主干的构成 计算器的布局 事件(即计算器上的按钮.文本框)监听 实现计算 2. 详细解释 假设我们的项目名为Calculator,而布局名称(Layout Name)为默认的activity_main .即设置如下图所示: 在这种前提下,有: 设置计算器布局的文件:Calculator/app/src/main/res/layout/activity_main.xml 事件监听和计算实现在同一个文件里:Calculat

C#Windows Form简易计算器实现(上)

第一次写博客,来分享一个简易计算器的代码.作为一名准程序员,就是要多写代码才能孰能生巧.重视基础知识才能飞的更快更高以及更稳. 代码可能会写的很糟糕,不完美不安全之处希望发现的越多越好 c#编写计算器带窗口的,对于新手来说是如何建立窗体以及实现按钮的响应事件吧!那么,首先来探索下窗口是怎么实现的吧! 步骤1:新建项目→C#windows窗体应用程序→新建解决方案 此时你会发现有两个名称,一个是解决方案名称,一个是项目名称.对于小程序来说其实没什么区别.但对于大点的程序最好就要区别开了.解决方案就

C++.NET的简易计算器的制作

计算器的制作需要实现一下几个功能:加减乘除,连续计算,重复计算. 加减乘除就是简单的二元运算,连续计算就是不使用等号连续进行几次二元运算,重复计算就是进行一次二元运算之后再次单击等号可以将之前的运算再次进行一次. 由于是C++的窗体程序,所以先设计出窗体的界面.界面如下. 需要注意的是,上面的两个显示框用的是TextBox,其余的均是Button. 窗体的FormBorderStyle属性应改成FixedSingle或其他,不能用None,这个样子计算器窗体框的大小就是固定不可变的. Maxim

函数调用_猜数字和简易计算器

package app1; import java.util.*; public class TestFunction{     public static void main(String[] args){         Scanner sc=new Scanner(System.in);         System.out.print("请选择一项应用:\n1.猜数字\n2.简易计算器");         int n=sc.nextInt();         switch(

基于mini2440简易计算器

基于mini2440简易计算器使用的是数组实现,并非逆波兰式,因此功能不够强大,仅供驱动学习,以及C语言基础编程学习之用.有时间读者可以用逆波兰式来实现强大功能计算器,原理也很简单,建议读<c程序设计第二版>里面有算法的代码.读者自行研究.此程序基于电子相册的改进,触摸屏,LCD,字符现实,数字输入等等 mini2440  索尼X35   LCD液晶屏 主函数部分: #include "def.h" #include "option.h" #includ

如何用jsp实现一个简易计算器(三)

做这个jsp页面,主要是为了实现在同一个页面提交和接受数据的功能. 这个小程序存在很多不足,希望大家多多批评指正. <%@ page language="java" contentType="text/html;" pageEncoding="gbk"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://w

C# 简易计算器

编写如下界面的简易计算器界面代码: using System; using System.Windows.Forms; using exp; namespace calculator { public partial class Form1 : Form { public Form1() { InitializeComponent(); } enum symbol { plus,dec,mult,div}; private void button1_Click(object sender, Ev