关于“计算题”程序的分析和总结

这次计算题程序已收官完成,再次进行分析和总结。

一、设计思路

1. 从txt读取算式。

2. 将算式由中缀转后缀。

3. 计算后缀算式并与用户输入答案进行比较,答案正确提示正确,答案错误提示错误并输出正确的答案。与此同时,统计用户正确与错误的次数以及题目的总数量。

4. 重复,直到算式全部计算完成,输出统计的正确错误数和题目的总数量。

二、具体实现

本程序运用了面向对象的设计思路。经过资料搜索,我发现有很多参考的程序。它们已经实现了中缀转后缀并输出正确结果并统计正误的功能,但无法计算分数。因此,我在此基础上进行了改进,增加了一个Fraction类。其把所有的整数都转换成了分数形式,并且重载了所有的运算符。之后,在重用的程序中把所有的定义的变量都定义为Fraction类型的就可以了。

以下是新增的Fraction类。

#pragma once
#include <iostream>

using namespace std;

class Fraction
{
public:
    Fraction();
    Fraction(int n);
    Fraction(int n, int d);
    ~Fraction();
    int numerator;
    int denominator;
    Fraction operator + (Fraction f);
    Fraction operator - (Fraction f);
    Fraction operator * (Fraction f);
    Fraction operator / (Fraction f);
    bool operator == (Fraction f);
    friend istream &operator >> (istream &in, Fraction &f);
    friend ostream &operator << (ostream &out, Fraction &f);
    void reduction();
    int get_gcd(int a, int b);
};
#include "stdafx.h"
#include "Fraction.h"

Fraction::Fraction()
{
}

Fraction::Fraction(int n) :numerator(n), denominator(1)
{
}

Fraction::Fraction(int n, int d) : numerator(n), denominator(d)
{
}

Fraction::~Fraction()
{
}

Fraction Fraction::operator + (Fraction f) {
    return Fraction(numerator * f.denominator + denominator * f.numerator, denominator * f.denominator);
}

Fraction Fraction::operator - (Fraction f) {
    return Fraction(numerator * f.denominator - denominator * f.numerator, denominator * f.denominator);
}

Fraction Fraction::operator * (Fraction f) {
    return Fraction(numerator * f.numerator, denominator * f.denominator);
}

Fraction Fraction::operator / (Fraction f) {
    return Fraction(numerator * f.denominator, denominator * f.numerator);
}

bool Fraction::operator == (Fraction f) {
    return numerator == f.numerator && denominator == f.denominator;
}

istream& operator >> (istream &in, Fraction &f) {
    in >> f.numerator;
    f.denominator = 1;
    char c;
    in.get(c);
    if (c != '
')
    {
        in >> f.denominator;
    }
    return in;
}

ostream& operator << (ostream &out, Fraction &f) {
    f.reduction();
    if (f.denominator == 1)
        out << f.numerator;
    else
        out << f.numerator << "/" << f.denominator;
    return out;
}

void Fraction::reduction() {
    int gcd = get_gcd(numerator, denominator);
    numerator = numerator / gcd;
    denominator = denominator / gcd;
    if (denominator < 0)
    {
        numerator = -numerator;
        denominator = -denominator;
    }
}

int Fraction::get_gcd(int a, int b)
{
    if (b == 0)
        return a;
    return get_gcd(b, a%b);
}

以下是网上参考的只能进行整数运算的程序的改进。

#pragma once
#include "Fraction.h"

class Expression
{
public:
    Fraction    Operand;            
    char        Operator  = NULL;            
};

#include "stdafx.h"
#include "Expression.h"


/////////////////////////////////////////////////////////////////////////
#pragma once
#include "Expression.h"
#include <stack>
#include <fstream>

class Calculator
{
private:
    stack<char> is;                                            
    stack<Fraction> ps;                                        
    Expression InfixExp[100];                                
    Expression PostfixExp[100];                               
    int il;                                                    
    int pl;                                                    
    bool GetInfixExp();                                        
    bool Transform();                                        
    bool GetTwoOperands(Fraction &opd1, Fraction &opd2);    
    bool Compute(char op);                                    
    ifstream ifs, ifs_line;
public:
    Calculator();
    ~Calculator();
    void Run();
};

#include "stdafx.h"
#include "Calculator.h"


Calculator::Calculator()
{
    Run();
}


Calculator::~Calculator()
{
}

bool Calculator::GetInfixExp() {
    char c;
    int newOperand;
    bool lastExpIsNum = false;
    bool nextNumAddMinus = false;

    char printExp[128];
    ifs_line.getline(printExp, 128);
    cout << printExp << " ";

    while (ifs >> c, c != '=') {
        switch (c) {
        case '-':
            if (lastExpIsNum)
            {
                InfixExp[il].Operator = c;
            }
            else
            {
                nextNumAddMinus = true;
                il--;
            }
            lastExpIsNum = false;
            break;
        case '+':
        case '*':
        case '/':
        case '(':
            InfixExp[il].Operator = c;
            lastExpIsNum = false;
            break;
        case ')':
            InfixExp[il].Operator = c;
            lastExpIsNum = true;
            break;
        default:
            if (c >= '0' && c <= '9') {
                ifs.putback(c);
                ifs >> newOperand;
                newOperand = nextNumAddMinus ? -newOperand : newOperand;
                nextNumAddMinus = false;
                InfixExp[il].Operand = Fraction(newOperand);
            }
            else {
                return false;
            }
            lastExpIsNum = true;
            break;
        }
        il++;
    }
    return true;
}

bool Calculator::Transform() {
    bool flag;                                           
    for (int i = 0; i < il; i++) {                        
        if (InfixExp[i].Operator == NULL) {                
            PostfixExp[pl].Operand = InfixExp[i].Operand;
            pl++;
        }
        else if (InfixExp[i].Operator == '(') {
            is.push('(');
        }
        else if (InfixExp[i].Operator == ')') {
            if (is.empty()) {
                return false;
            }
            else {
                flag = false;                           
                while (!is.empty()) {
                    if (is.top() != '(') {
                        PostfixExp[pl].Operator = is.top();
                        is.pop();
                        pl++;
                    }
                    else {
                        flag = true;
                        is.pop();
                        break;
                    }
                }
                if (is.empty() && !flag) {
                    return false;
                }
            }
        }
        else {
            while (
                !is.empty() &&
                is.top() != '(' &&
                !((is.top() == '+' || is.top() == '-') && (InfixExp[i].Operator == '*' || InfixExp[i].Operator == '/'))
                ) {
                PostfixExp[pl].Operator = is.top();
                is.pop();
                pl++;
            }
            is.push(InfixExp[i].Operator);
        }
    }
    while (!is.empty()) {
        if (is.top() == '(') {
            return false;
        }
        else {
            PostfixExp[pl].Operator = is.top();
            is.pop();
            pl++;
        }
    }
    return true;
}

bool Calculator::GetTwoOperands(Fraction& opd1, Fraction& opd2) {
    if (ps.empty())
        return false;
    opd1 = ps.top();
    ps.pop();
    if (ps.empty())
        return false;
    opd2 = ps.top();
    ps.pop();
    return true;
}

bool Calculator::Compute(char op) {
    bool result;
    Fraction operand1, operand2;
    result = GetTwoOperands(operand1, operand2);
    if (result) {
        switch (op)
        {
        case '+':
            ps.push(operand2 + operand1);
            break;
        case '-':
            ps.push(operand2 - operand1);
            break;
        case '*':
            ps.push(operand2 * operand1);
            break;
        case '/':
            if (operand1.numerator == 0) {
                cout << "除数存在0,错误!" << endl;
                return false;
            }
            else {
                ps.push(operand2 / operand1);
            }
            break;
        }
    }
    return true;
}

void Calculator::Run() {
    ifs = ifstream("Expressions.txt");
    ifs_line = ifstream("Expressions.txt");

    Fraction correct_answer, user_answer;    
    int    correct_num = 0, wrong_num = 0;

    // 输入中缀表达式
    cout << "请计算下列算式:" << endl;
    while (!ifs_line.eof())
    {
        il = 0;
        pl = 0;
        while (!is.empty())
            is.pop();
        while (!ps.empty())
            ps.pop();
        for (int i = 0; i < 100; i++)
        {
            InfixExp[i] = PostfixExp[i] = Expression();
        }

        if (GetInfixExp() && Transform()) {
            for (int i = 0; i < pl; i++) {
                if (PostfixExp[i].Operator == NULL)
                    ps.push(PostfixExp[i].Operand);
                else {
                    if (!Compute(PostfixExp[i].Operator))
                        return;
                }
            }
            correct_answer = ps.top();
        }
        else
        {
            cout << "算式格式错误" << endl;
            return;
        }

        cin >> user_answer;
        user_answer.reduction(), correct_answer.reduction();
        if (user_answer == correct_answer)
        {
            correct_num++;
            cout << "正确" << endl;
           
        }
        else
        {
            wrong_num++;
            cout << "错误,正确答案是" << correct_answer << endl;
           
        }
    }
    cout << "--------------------------" << endl;
    cout << "" << correct_num + wrong_num << "题,";
    cout << "正确" << correct_num << "道,错误" << wrong_num << "" << endl;
}

三、项目总结

这次项目,对我改进程序的能力有了提升。每个程序并不是一定要从零开始。我们可以利用他人的的思路和成果,再次基础上进行改进。同时,我们也应多和他人进行交流,因为这样可以更方便地获取新鲜高端的思路,对项目的完成有所广益。

原文地址:https://www.cnblogs.com/xDan/p/5295773.html