题目1:完成一个命令行的四则运算程序

前言

  刚刚看到这个题目的时候,我在想,不就是一个加减乘除的程序么?分分钟就写完了。等我看到需求的时候,我头都大了一圈……。我将需求分成了几个需要解决的问题:

  1:支持分数运算。

  2:表达式生成与计算。

  3:判断正误。

正文

  首先,是如何支持分数运算。既然要支持分数运算,那么肯定要表示分数,所以就要创建一个分数类。而分数和整数是要混合运算的,那么显然,整数其实也是可以表现成分数。于是有了下面的类。在类中对操作符进行重载,便于计算表达式。在运算时自动执行transFrac()成员函数以保证计算后得到分数分子分母都不可再约分,以便输出时判断是按整数输出还是按分数输出。

class Num{
public:
    Num(int numerator=0, int denominator = 1){
        a = numerator;
        b = denominator;
        int p;
        p = (a + b - abs(a - b)) / 2;
        for (int i = 2; i <= p; ++i){
            if (a%i == 0 && b%i == 0){
                a /= i;
                b /= i;
                i = 2;
            }
        }
    }

    int getNume(){
        return a;
    }

    int getDeno(){
        return b;
    }

    void transFrac(Num &fra){
        int p;
        p = (fra.a + fra.b - abs(fra.a - fra.b)) / 2;
        for (int i = 2; i <= p; ++i){
            if (fra.a%i == 0 && fra.b%i == 0){
                fra.a /= i;
                fra.b /= i;
                i = 2;
            }
        }

    }

    Num operator+(const Num rhs){
        Num result;
        result.b = b*rhs.b;
        result.a = a*rhs.b + rhs.a*b;
        transFrac(result);
        return result;
    }

    Num operator-(const Num rhs){
        Num result;
        result.b = b*rhs.b;
        result.a = a*rhs.b - rhs.a*b;
        transFrac(result);
        return result;
    }

    Num operator*(const Num rhs){
        Num result;
        result.a = a*rhs.a;
        result.b = b*rhs.b;
        transFrac(result);
        return result;
    }

    Num operator/(const Num rhs){
        Num result;
        result.a = a*rhs.b;
        result.b = b*rhs.a;
        transFrac(result);
        return result;
    }

    bool operator==(const Num rhs){
        if (a == rhs.a&&b == rhs.b)
            return true;
        else
            return false;
    }


    Num operator=(const int rhs){
        a = rhs;
        b = 1;
        return *this;
    }
    Num operator=(const Num rhs){
        a = rhs.a;
        b = rhs.b;
        return *this;
    }
private:
    int a;
    int b;

};
classNum

  其次,是表达式生成与计算。如果是任意生成的表达式,那么在计算表达式的值的时候,就需要在表达式求值上做大量的工作,如判断优先级,通过栈等转化表达式,最后才是计算得到结果。时间有限,我就使用了比较低端的方法,那就是固定表达式的格式,然后通过分配各个位置上的值和运算符来完成简易的表达式生成,并通过检查结果来判断两个表达式是否重复。将生成的表达式存储在题库中等待完成。

void setProbAndAns(int proNum, int proLevel){
    ofstream ofsQues;
    ofstream ofsAnsw;
    ofsQues.open("Questions.txt", ofstream::out);
    ofsAnsw.open("Answer.txt", ofstream::out);
    char Expression[100];
    char Answer[10];
    srand((unsigned)time(NULL));
    int a, b, c, d, e1, e2, e3;
    Num lhs, rhs, res;
    for (int i = 0; i < proNum; ++i){
        vector<Num>A(proNum);
        vector<Num>B(proNum);
        vector<Num>C(proNum);
        vector<Num>D(proNum);  //4 position of number
        vector<Num>R(proNum);  //result
        a = rand() % proLevel;
        b = rand() % proLevel+1;
        c = rand() % proLevel;
        d = rand() % proLevel+1;
        A[i] = a;
        B[i] = b;
        C[i] = c;
        D[i] = d;

        vector<char>E1(proNum);
        vector<char>E2(proNum);
        vector<char>E3(proNum);
        e1 = rand() % 4;
        e2 = rand() % 2;
        e3 = rand() % 4;

        switch (e1){
        case 0:
            E1[i] = '+';
            lhs = A[i] + B[i];
            break;
        case 1:
            E1[i] = '-';
            lhs = A[i] - B[i];
            break;
        case 2:
            E1[i] = '*';
            lhs = A[i] * B[i];
            break;
        case 3:
            E1[i] = '/';
            lhs = A[i] / B[i];
            break;
        }
        switch (e3){
        case 0:
            E3[i] = '+';
            rhs = C[i] + D[i];
            if (e2 == '-')
                rhs = C[i] - D[i];
            break;
        case 1:
            E3[i] = '-';
            rhs = C[i] - D[i];
            if (e2 == '-')
                rhs = C[i] + D[i];
            break;
        case 2:
            E3[i] = '*';
            rhs = C[i] * D[i];
            break;
        case 3:
            E3[i] = '/';
            rhs = C[i] / D[i];
            break;
        }
        switch (e2){
        case 0:
            E2[i] = '+';
            res = lhs + rhs;
            break;
        case 1:
            E2[i] = '-';
            res = lhs - rhs;
            break;
        }
        if (res.getNume() < 0){
            i -= 1;
            continue;
        }
        else{
            R[i] = res;
        }
        //ganrantee no repeat
        int flag = 0;
        for (int j = 0; j < i; ++j){
            if (R[i] == R[j]){
                flag = 1;
                break;
            }
        }
        if (flag == 1){
            i -= 1;
            continue;
        }

        sprintf_s(Expression, "%d %c %d %c %d %c %d = ", A[i].getNume(), E1[i], B[i].getNume(), E2[i], C[i].getNume(), E3[i], D[i].getNume());
        if (R[i].getDeno() == 1||R[i].getNume() == 0)
            sprintf_s(Answer, "%d:%d", i + 1, R[i].getNume());
        else
            sprintf_s(Answer, "%d:%d/%d", i + 1, R[i].getNume(), R[i].getDeno());

        ofsQues << Expression << endl;;
        ofsAnsw << Answer << endl;
    }

    ofsQues.close();
    ofsAnsw.close();

}
setProblem

  最后,是判断正误。在题库生成的同时,答案也同时生成。此时程序会等待用户在题库中填写答案。填写完毕后输入1得到结果。由于有分数的存在,所以我通过比较题库中=后面略过所有空白符后的字符串与答案:后面的字符串来对比是否正确,正确的题号会被记录,最终格式化输出置Grade文件里保存起来以供用户查看。

void setGrade(int proNum){
    ifstream ifsQus;
    ifstream ifsAns;
    ofstream ofsGra;
    ifsQus.open("Questions.txt",ifstream::in);
    ifsAns.open("Answer.txt",ifstream::in);
    ofsGra.open("Grade.txt",ofstream::app);
    char ques[100];
    char answ[10];
    char grade[500];
    int gra;
    vector<int>right(proNum);
    int i,j;
    int a, b;
    for (i = 0; i < proNum; ++i)
        right[i] = 0;
    for (i = 0,j=0; i < proNum;++i){
        ifsQus.getline(ques, 100);
        ifsAns.getline(answ, 10);
        for (a = 0; ques[a] != '='; ++a)
            ;
        for (b = a; ques[b] != ' '; ++b)
            ;
        for (a = 0; answ[a] != ':'; ++a)
            ;


        if (isCorrect(b+1,a+1,ques,answ)){
            j++;
            right[i] = 1;
        }
    }
    vector<int>rightPos(j);
    for (i = 0,j=0; i < proNum; ++i){
        if (right[i] == 1){
            rightPos[j] = i;
            ++j;
        }
    }
    char num[10];
    sprintf_s(grade,"Correct:%d( ",j);
    for (i = 0; i < j; ++i){
        sprintf_s(num,"%d ",rightPos[i]+1);
        strcat_s(grade,num);
    }
    strcat_s(grade, ")");

    ofsGra << grade << endl;
    ifsQus.close();
    ifsAns.close();
    ofsGra.close();
}
setGrade

  在主函数中提供了选项让用户选择生成什么样规模和数量的问题,由于我觉得不会有人一下子做几百道这种题,所以我并没有写出那些离谱的选项,当然程序本身其实是支持生成上千上万道题的(笑)。

int main(void)
{
    int option;
    int proNum;
    int proLevel;

    cout << "Please choose the number of the problem:" << endl;
    cout << "1.10                               2.100" << endl;
    cout << "3.200                              4.500" << endl;
    cin >> option;
    switch (option){
    case 1:
        proNum = 10;
        break;
    case 2:
        proNum = 100;
        break;
    case 3:
        proNum = 200;
        break;
    case 4:
        proNum = 500;
        break;
    default:
        proNum = 50;
        break;
    }
    cout << "Please choose the level of the problem:" << endl;
    cout << "1.10                               2.50" << endl;
    cout << "3.100                                  " << endl;
    cin >> option;
    switch (option){
    case 1:
        proLevel = 10;
        break;
    case 2:
        proLevel = 50;
        break;
    case 3:
        proLevel = 100;
        break;
    default:
        proLevel = 30;
        break;
    }

    setProbAndAns(proNum, proLevel);
    cout << "Please write the  answers in the Questions.txt and save." << endl;
    cout << "Enter 1 to get the Grade" << endl;
    cin >> option;
    while (option != 1){
        cout << "Please enter again:" << endl;
        cin >> option;
    }
    setGrade(proNum);
    cout << "Open the Grade.txt to check your grade!" << endl;
    Sleep(10000);

    return 0;
}
main

  这个程序还有很多很多不完善的地方,尤其是生成表达式和计算表达式那一块,完全可以更好更完善,这就有待以后有时间在github上慢慢改进了(笑)。

  完整程序地址:https://github.com/MorriganMesser/ArithmeticBank

  博客编辑:尉智辉

posted @ 2016-09-11 15:46  Goliath  阅读(319)  评论(0编辑  收藏  举报