1. 程式人生 > >專案——簡易計算器

專案——簡易計算器

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