• 四则运算基本功能完成


    一、基本功能的实现

    在项目目录下新建txt文件,输入若干四则运算式,每个算式以“=”结束。

    运行结果:

    输入的答案可以是整数、分数。输入没有化简的分数算作正确。程序给出的正确答案是化简后的分数。

    二、较第一版程序的改动

    增加了两个类:Scanner类,用于处理算式;fraction类,表示分数。

    Scanner类定义:

     1 class Scanner {
     2 private:
     3     int mSize;        //单词流的最大长度
     4     int lsize;        //单词流的实际长度
     5     string *lex;    //分割后的单词流
     6 public:
     7     string buffer;    //保存从文件读入的算式
     8 
     9     Scanner() {
    10         mSize = MAXWORD;
    11         lsize = 0;
    12         lex = new string[mSize];
    13     }
    14     int priority(const char op);    //求参数符号的优先级
    15     bool analyzer();                //词法分析
    16     string *InfixExpToPostfixExp();    //中缀表达式转后缀表达式
    17 };

    fraction类定义:

     1 class fraction {    //分数类
     2 private:
     3     int numerator;    //分子
     4     int denominator;//分母
     5 public:
     6     fraction() {
     7         numerator = 0;
     8         denominator = 1;
     9     }
    10     fraction(int num, int den) {
    11         numerator = num;
    12         denominator = den;
    13     }
    14     void setFraction(int a, int b);
    15     fraction getFraction();
    16 
    17     void operator=(const fraction &scd);                //
    18     bool operator==(const fraction &scd);                //
    19     fraction operator+(const fraction &scd);            //
    20     fraction operator-(const fraction &scd);            //    重载运算符
    21     fraction operator*(const fraction &scd);            //
    22     fraction operator/(const fraction &scd);            //
    23     friend ostream& operator << (ostream&, fraction&);    //
    24 
    25     void inputF();            //从标准输入键入分数值
    26     int gcd(int a, int b);    //求最大公约数
    27     void simplify();        //分数化简
    28 };

    项目要求中有一项:

    2.出现真分数和假分数的运算

    学长在课上提出的具体要求是:算式中可以出现分数,用户输入的答案可以是分数,程序给出的答案以分数形式出现。

    我最初的思路是,算式中的分数‘/’在计算上与除法‘÷’没有区别,那么程序不用特地处理分数,按照正常四则运算计算出一个实数答案即可。用户输入分数后,程序算出相应实数值并进行比较。

    这种处理方式唯一的问题是程序给出正确答案时需要用分数表示,如何将实数转换为分数?

    最直接的方法是用小学数学的知识:如何把无限循环小数弄成分数?

    但是我发现当分母过大时,计算机显然不能存储太长的小数值,因此我决定将输入的数统一转换成分数形式,增加处理分数的fraction类。当负责计算结果的函数处理表达式时,将遇到的操作数变成分母为1的分数。按照分数的四则运算方法计算结果,然后化简为最简分数。

    文件读取操作在main函数中实现。

    下面给出main函数的代码:

     1 int _tmain(int argc, _TCHAR* argv[])
     2 {
     3     string *PostfixExp;            //后缀表达式
     4     fraction UserResult,CurrectResult;    //用户输入结果和正确答案
     5     fraction score;    //存储成绩
     6     Scanner equation;    //存储算式
     7     int testNum = 0, currectNum = 0;    //当前题目数和答对数
     8     ifstream inputFile("test.txt", ios::in);    
     9     if (!inputFile.is_open()) {
    10         cout << "can't open file!" << endl;
    11         return 0;
    12     }
    13     score.setFraction(0, 0);    //分数初始化
    14     while (!inputFile.eof()) {    //读取文件内容,直到遇到文件结束符
    15         equation.buffer.clear();    //清空buffer
    16         getline(inputFile, equation.buffer);    //读取新的一行
    17         if (!equation.analyzer()) {                //处理词法分析失败
    18             cout << "wrong equation" << endl;
    19         }
    20         else {
    21             testNum++;
    22             PostfixExp = equation.InfixExpToPostfixExp();    //将中缀表达式转换为后缀表达式
    23             CurrectResult = MainCalculate(PostfixExp);    //保存计算结果
    24             CurrectResult.simplify();    //化简结果
    25             cout << equation.buffer << endl << "input your result:" << endl;
    26             UserResult.inputF();        //用户输入结果
    27             UserResult.simplify();        //化简用户的结果
    28             if (UserResult == CurrectResult) {    //如果答案正确,得分+1
    29                 currectNum++;
    30                 score.setFraction(currectNum, testNum);
    31                 cout << "currect! score:" << score << endl;
    32             }
    33             else {                                //如果答案不正确,输出正确答案,不加分
    34                 score.setFraction(currectNum, testNum);
    35                 cout << "wrong! anwser:" << CurrectResult << "score:" << score << endl;
    36             }
    37         }
    38     }
    39     inputFile.close();    //关闭文件
    40     return 0;
    41 }

    三、遇到的问题与解决方法


    最早遇到的问题是在main函数中,我只定义一个Scanner类存储算式,每次读入的算式会覆盖掉上一次读入的算式。但是运行后程序显示出的“正确答案”是错的,而且能看出和前一个算式有

    关。我分析认为虽然buffer中的内容清空了,但是类成员lex[]的内容没有清空,如果第二次输入的算式比第一次短,那么lex[]中就会留有上一次算式的结果。因此在成员函数

    string *InfixExpToPostfixExp();的结尾加上

    1     for (int i = 0; i < mSize; i++)    //清空单词流
    2         lex[i].clear();

    问题得到解决。

    第二个遇到的问题是编译时出现错误提示:

    原因是我在fraction类中重载插入符“>>”,想用

     os >> out.numerator >> "/" >> out.denominator >> endl;

    直接将输入的字符串变为分数,看来编译器并不承认我想当然的写法:D

    后来我在网上查找了各种重载插入符的文章,都没有实现格式化输入的,我只好写一个void inputF();手动将输入的字符串转为分数。

     1 void fraction::inputF() {
     2     unsigned int pos;
     3     string str;
     4     size_t sz;
     5     this->setFraction(0, 0);
     6     cin >> str;
     7     for (pos = 0; pos < str.length() && str[pos] != '/'; pos++);    //将指针移到输入流的‘/’位置,或结果为整数时移到末尾
     8     if (pos == 0) {    //没有分子的情况
     9         cout << "fraction illegal." << endl;
    10         exit(0);
    11     }
    12     else if (pos == str.length()) {    //没有分母的情况
    13         this->numerator = stoi(str);
    14         this->denominator = 1;
    15     }
    16     else if (str[pos] == '/') {    //有分子分母的情况
    17         //in.setFraction(stoi(str, &sz, 10), stoi(str.substr(sz + 1), nullptr, 10));
    18         this->numerator = stoi(str, &sz);
    19         this->denominator = stoi(str.substr(sz + 1));
    20     }21 }

    我觉得这并不是很好的解决方法,应该有方法可以通过重载插入符直接实现cin>>fraction对象,但是还没有找到,今后会继续找解决办法。

    在写这个函数时还遇到一个bug卡了我十分钟,错误现象是输入的分数答案总是判断为错误。将函数分段测试才发现原因是

    if(pos == str.length())写成了if(pos = str.length())

    没想到还会犯这种初级错误,而且盯了十分钟都没看出来!测试时发现函数生成的分数只有分子,分母一直是1,这才发现是因为pos指针直接被赋值跳到了字符串的结尾。而函数stoi(str)

    会自动截断非数字的字符,因此得出的分数只有‘/’符号前的分子。

    我认为程序设计中思维缜密非常重要,今后必须注意这种细节上的区别。

    四、总结


    作为第一次个人项目,我觉得难度适中,但我自己的编程速度太慢了,如果还有时间,我希望能试着实现扩展功能。

  • 相关阅读:
    java注解
    Mac窗口管理管理软件SizeUp
    mac下8080端口到80端口的转发
    mac svn的替代品CornerStone
    模板模式讲解二
    数组和集合List的相互转化
    fastjson使用示例
    模板模式讲解一
    mybatis-generator-core自动生成do、mapping、dao 代码
    maven依赖传递关系
  • 原文地址:https://www.cnblogs.com/lifangda/p/5277642.html
Copyright © 2020-2023  润新知