软件工程第四次作业-3四则运算

结对同学:姜珊

 

目录:

1.需求分析

2.基本设计

3.代码说明

4.测试运行

5.重难点知识

6.缺点改进及体会

 

1.需求分析:

       作业链接:https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/997

  功能一和功能二其实可以合并成为一个功能,就是计算带括号的四则运算式,并对结果进行判断和输出。

  功能三是利用输入参数来进行出题并输出。

2.基本设计

       由需求分析可以得到,可以根据参数个数来执行某一程序段。

       对于功能一二,只有一个参数就是文件名,直接输入f4并回车即可运行。

       对于功能三,要输入三个参数如:f4 –c 2,其中-c后边的数字要当作程序体中的循环次数,完成输出个数的限定。

 

3.代码说明

       地址:https://git.coding.net/immixiaomi/f4-mhjs.git

       首先定义了字符类型的栈及相关操作,在将中序表达式转化为后序表达式时要用到:

typedef int Status;
typedef char ElemType;
typedef struct 
{
    ElemType data[MAXSIZE];
    int top;//栈顶指针
}Stack;

Status InitStack(Stack *S)//初始化
{
    int i;
    for(i = 0; i < MAXSIZE; i++)
    S->data[i] = NULL;
    S->top = -1;
    return 1;
}

Status CreateStack(Stack *S,int n)//创建一个长度为n的堆栈
{
    if(n>MAXSIZE || n<1)
    {
        printf("输入长度有误!
");
        return -1;
    }
    srand(time(0));
    int i;
    for(i = 0; i < n; i++)
    {
        S->data[i] = rand()%100+1;
    }
    S->top = n-1;
    return 1;
}

Status push(Stack *S,ElemType e)//压栈操作
{
    if(MAXSIZE-1 == S->top)
    {
        printf("栈已满
");
        return -1;
    }
    ++(S->top);
    S->data[S->top] = e;
    return 1;
}

Status pop(Stack *S,ElemType *e)//出栈
{
    if(-1 == S->top){
        printf("栈为空!
");
        return -1;
    }
    *e = S->data[S->top];
    --(S->top);
    return 1;
}

  GetStr()函数是自动生成字符串的函数,参数是空字符数组,通过随机数的产生,并将随机数用itoa()函数转化成字符型,加入随机的运算符产生了中序表达式。先产生了表达式再向其中加入括号,括号都是成对加入,完成了带括号表达式的构建。

void GetStr(char *str)//构建随机串,并添加括号 
{
    char str1[1] = "";
    int a, b, c, d;
    char op1, op2, op3;
    int i;
    srand((int)time(0));
    a = (int)(rand()%10);//四个变量 
    b = (int)(rand()%10);
    c = (int)(rand()%10);
    d = (int)(rand()%10); 
    i = (int)(rand()%4);//三个操作符 
    switch(i)
    {
        case 0:
            op1 = '+'; break;
        case 1:
            op1 = '-'; break;
        case 2:
            op1 = '*'; break;
        case 3:
            op1 = '/'; break;
    }
    i=(int)(rand()%4);
    switch(i)
    {
        case 0:
            op2 = '+'; break;
        case 1:
            op2 = '-'; break;
        case 2:
            op2 = '*'; break;
        case 3:
            op2 = '/'; break;
    }
    i=(int)(rand()%4);
    switch(i)
    {
        case 0:
            op3 = '+'; break;
        case 1:
            op3 = '-'; break;
        case 2:
            op3 = '*'; break;
        case 3:
            op3 = '/'; break;
    }
    itoa(a, str1, 10);
    strcpy(str, str1);
    str[1] = op1;
    itoa(b, str1, 10);
    strcat(str, str1);
    str[3] = op2;
    itoa(c, str1, 10);
    strcat(str, str1);
    str[5] = op3;
    itoa(d, str1, 10);
    strcat(str, str1);
    for(int j = 0; j < 7; j++)
    {
        if(str[j] == '/')
        {
            int e;
            e = (int)(rand()%9)+1;
            itoa(a, str1, 10);
            str[j+1] = str1[0];
        }
    }
    int j, k;//左括号右括号 
    int flag = 0;
    j = (int)(rand()%3);
    int length = 8;
    if(j == 0)//位置1加不加左括号的判定 
    {
        k = (int)(rand()%4);
        if(k != 3&&flag != 2)
        {
            for(int i = length;i > 0;i--)//向坐标0处插入括号 
            {
                str[i] = str[i-1];
            }
            str[0] = '(';
            flag++;
            length++;
            int l = (int)(rand()%4);
            if(l < 2)
            {
                for(int i = length; i > 4; i--)
                {
                    str[i] = str[i-1];
                }
                str[4] = ')';
                flag--;
                length++;
                for(int i = length;i > 6;i--)
                {
                    str[i] = str[i-1];
                }
                str[6] = '(';
                flag++;
                length++;
                for(int i = length;i > 10;i--) 
                {
                    str[i] = str[i-1];
                }
                str[10] = ')';
                flag--;
                length++;
            }
            else if(l = 2)
            {
                for(int i = length; i > 4; i--) 
                {
                    str[i] = str[i-1];
                }
                str[4] = ')';
                flag--;
                length++;
            }
            else if(l = 3)
            {
                for(int i = length; i > 6; i--)//插入括号 
                {
                    str[i] = str[i-1];
                }
                str[6] = ')';
                flag--;
                length++;
            }

        }
        else if(k != 2&&flag != 2)
        {
            for(int i = length+1; i > 1; i--)//向坐标0,1处插入俩左括号 
            {
                str[i] = str[i-2];
            }
            str[0] = '(';
            str[1] = '(';
            flag = flag+2;
            length = length+2;
            
            for(int i = length; i > 5; i--)
            {
                str[i] = str[i-1];
            }
            str[5] = ')';
            flag--;
            length++;
            
            for(int i = length; i > 8; i--)
            {
                str[i] = str[i-1];
            }
            str[8] = ')';
            flag--;
            length++;
        }
    }
    
    else if(j == 1)
    {
        k=(int)(rand()%4);
        if(k != 3&&flag != 2)
        if(0)
        {
            for(int i = length; i > 2; i--)//向坐标2处插入括号 
            {
                str[i] = str[i-1];
            }
            str[2] = '(';
            flag++;
            length++;
        }
        else if(k != 2&&flag != 2)
        {
            for(int i = length+1; i > 3; i--)//向坐标2,3处插入俩左括号 ,只能接着向6和8(原坐标)添加括号 
            {
                str[i] = str[i-2];
            }
            str[2] = '(';
            str[3] = '(';
            flag = flag+2;
            length = length+2;
            
            for(int i = length; i > 7; i--)
            {
                str[i] = str[i-1];
            }
            str[7] = ')';
            flag--;
            length++;
            for(int i = length;i > 10;i--)
            {
                str[i] = str[i-1];
            }
            str[10] = ')';
            flag--;
            length++;
        }
    }
    else if(j == 2)
    {
        k = (int)(rand()%4);
        if(k != 3&&flag != 2)
        {
            for(int i = length; i>4; i--)//向坐标4处插入括号 
            {
                str[i] = str[i-1];
            }
            str[4] = '(';
            flag++;
            length++;
            //str4 插入左括号一个
            for(int i = length; i>8; i--)
            {
                str[i] = str[i-1];
            }
            str[8] = ')';
            flag--;
            length++;
        }
    }
    
    for(i = 0; i < length-1; i++)
    {
        printf("%c",str[i]);
    }
    printf("="); 
}
View Code

  Translate()函数是将中序表达式转化为后序表达式,参数是两个字符串,分别是旧串和转化后的新串。用栈进行操作,并对括号进行配对取出,转化成为没有括号的逆波兰表达式。

int Translate(char *str,char *exp)//中缀表达式转后缀表达式
{
    //新建一个栈,来存储符号
    char e;
    Stack S;
    if(InitStack(&S) != 1)
    {
        printf("初始化栈失败!
");

    }
    //当带转换的字符串*mid未终止时,循环处理
    while(*str)
    {
            //如果是数字,则直接输出
        if(*str >= '0' && *str <= '9')
        {
            *(exp++) = *(str++);
            continue;
        }else if(*str == '+' || *str == '-' || *str == '*' || *str == '/' || *str == '(' || *str == ')')
        {
            //输入的是合法运算符号,比较之前是否有更高优先级的符号
            if(S.top == -1 || '(' == *str)
            {
                //当符号栈为空或遇到左括号时,符号入栈
                push(&S, *(str++));
                continue;
            }
            if(')' == *str)
            {
                //遇到右括号时,栈顶元素依次出栈;直到遇到第一个左括号时结束
                pop(&S, &e);
                *(exp++) = e;
                while(pop(&S, &e) && e != '(')
                {
                    *(exp++) = e;
                }
               // printf("%c
",e);
                str++;

                continue;
            }
            //后续的处理都要取出临时的栈顶元素,与当前输入的符号*mid相比较;当临时栈顶元素优先级大于等于输入符号的优先级时,出栈;否则符号入栈(已经弹出一个,记得把弹出的元素也入栈)
            pop(&S,&e);
            if('+' == *str || '-' == *str)
            {
                if(e == '(')
                {
                    push(&S, '(');
                    push(&S, *(str++));
                    continue;
                }
                else
                {
                    *(exp++) = e;
                    push(&S, *(str++));
                    continue;
                }
            }
            else if('*' == *str || '/' == *str)
            {
                if('*' == e || '/' == e)
                {
                    *(exp++)=e;
                    push(&S, *(str++));
                    continue;
                }
                else
                {
                    push(&S, e);
                    push(&S, *(str++));
                    continue;
                }
            }

        }
        else
        {
            printf("error%c
", *str);
            return -1;
        }
    }
    //当待转换的字符已经结束时,符号栈至少还有一个元素(中缀表达式的特点:数字结尾;后缀表达式以符号结尾);将栈中的元素依次出栈
    while(S.top != -1)
    {
        pop(&S, &e);
        *(exp++) = e;
    }
    //字符串的结束符!
    *exp = '';
}

  Calculate()函数是将后缀表达式进行计算,返回值类型为double型,值为该表达式的结果。

double Calculate(char* exp)//计算逆波兰表达式的值 
{

    int i = 0;
    double temp[MAXSIZE];
    int top = -1;
    double a;
    while(exp[i] != '')
    {
        if(exp[i]>='0' && exp[i]<='9')
        {
            temp[++top] = exp[i]-'0';
        }
        else if(exp[i] == '-')
        {
            double m = temp[top];
            top--;
            double n = temp[top];
            top--;
            temp[++top] = n-m;
            
            
        }
        else if(exp[i] == '+')
        {
            double m = temp[top];
            top--;
            double n = temp[top];
            top--;
            temp[++top] = n+m;
        }
        else if(exp[i] == '/')
        {
            double m = temp[top];
            top--;
            double n = temp[top];
            top--;
            temp[++top] = n/m;
        }
        else if(exp[i] == '*')
        {
            double m = temp[top];
            top--;
            double n = temp[top];
            top--;
            temp[++top ] = n*m;
        }
        i++;
    }

    return temp[top];
}

  输出有两个函数,PrintOnScr()负责功能一二的输出,PrintOnFile()负责功能三的输出,但是功能三因技术原因未完成,暂时只输出至屏幕。

void PrintOnScr(double answer,int count)//输出至屏幕,对应功能一二 
{
    printf("
");
    printf("?");
    double ans;
    scanf("%lf",&ans);
    if(answer == ans)
    {
        printf("答对啦,你真是个天才!");
        count++;
        //break;
    }
    else
    {
        printf("再想想吧,答案似乎是%d喔!",answer);
    }
    printf("
");
}


void PrintOnFile(double answer)//输出至文件,对应功能三 
{
    printf("			%d
",answer);
    return;
}

  主函数里定义两个空串,用于传入函数中接收数据,然后根据参数个数执行对应某段函数体。

int main(int argc, char *argv[])
{
    char str[MAXSIZE]="";
    char final[MAXSIZE]="";
    double answer;
    int count = 0;
    if(argc == 1)
    {
        
        for(int i = 0; i < 20; i++)
        {
            GetStr(str);
            Translate(str,final);
            answer=Calculate(final);
            PrintOnScr(answer,count);
            fflush(stdin);
        }
        printf("
");
        printf("你一共答对%d道题,共20道题。", count); 
        return 0;    
    }
    else if(argc == 3)
    {
        int n = atoi(argv[2]);
        if(n < 0)
        {
            printf("题目数量必须是 正整数。");
        }
        else if(argv[2][0] >= 'a'&&argv[2][0]<='z')
        {
            printf("题目数量必须是 正整数。");
        }
        else 
        {
            for(int i = 0; i < 100; i++)
            {
                for(int j = 0; j < 100; j++)
                {
                    if(argv[i][j] == '.')
                    {
                        printf("题目数量必须是 正整数。");
                        return 0;
                    }
                }
            }
            
            for(int i = 0; i < n; i++)
            {
                GetStr(str);
                Translate(str,final);
                answer=Calculate(final);
                PrintOnFile(answer);
            }    
        }
    }
}

4.测试运行

  功能一和功能二:

  只能算最多2道题,然后程序崩溃了。我估计是堆栈溢出的错误,但是不知如何清空内存,因为在刚刚打开窗口的时候程序是正常的。无法修改这个错误。

  功能三:

  可以正确判断-c后边的参数,却无法进行输出,程序再次崩溃。

5.重难点知识

  本次编程用到了以下知识,参考链接如下:

  C语言产生随机数:http://www.cnblogs.com/qjziyou/p/4782454.html

  Itoa函数:http://www.cnblogs.com/bluestorm/p/3168719.html

  定义字符栈的方法:http://blog.csdn.net/keepupblw/article/details/26343769

  判断整数:http://blog.csdn.net/youngdze/article/details/13628877

  浮点数转换成ASCII码:http://download.csdn.net/download/letre/1936905

 

6.缺点改进及体会

       这次的三个功能都没有完全实现,我和姜珊同学对需求的考虑不充分,没有提前测试一下循环情况下程序会不会溢出,结果真的发生了溢出,也尝试过修改但是并没有奏效。在字符栈那里的使用肯定没有想象的那么顺利。最开始我们一起讨论代码规范的书写,互相交流了风格,发现了我们其实并没有什么风格,就制订了一份详细的规范我们仔细遵守就好。此外,我和姜珊同学对于函数的参数设置进行了争论,最后一致决定采用字符串传参数。在Calculate()函数这里我们讨论了一下是否要使用atoi()函数,我认为采用减去’0’的效果更好更简洁,最后我们决定使用后者的方法。在遇到字符栈输出溢出的时候,我们试着重新写了一个新的函数,遗憾的是,效果还没有原来的好,根本不能运行,直接崩溃。在最后判断浮点数的时候,我提出了将其转化为ASCII码的建议,姜珊同学认为直接搜索小数点即可,我们采用了她的方法。

       以后要注意对代码段的掌控,不要等到错误无法挽回在试图修改。

  照片:

原文地址:https://www.cnblogs.com/immixiaomi/p/7650450.html