首页 > 代码库 > 题目1:完成一个命令行的四则运算程序

题目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

  博客编辑:尉智辉

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