【软件工程基础结队项目】四则运算题目生成

【软件工程基础结对项目】四则运算题目生成

  • Github 项目地址
  • 任务
  • 时间预估
  • 需求分析
    • 模块划分
  • 设计实现
  • 关键代码
  • 时间统计

Github 项目地址

Github: acromema/CPGenerator

任务

写一个能自动生成小学四则运算题目的命令行软件,满足以下要求:

  • 一次可以出一千道题目,并且没有重复的,把题目写入一个文件中。
  • 当有多余一个运算符的时候,对表达式求值。
  • 除整数以外,还要支持真分数的四则运算。
  • 让程序能接受用户输入答案,并判定对错。最后给出总 对/错 数量。
  • 支持乘方运算。
  • 把程序变成一个 Windows/Mac/Linux电脑图形界面程序。
  • 增加“倒计时”功能,每个题目必须在20s内完成,如果完不成,则得0分并进入下一题。
  • 增加“历史记录”功能,把用户做题的成绩记录下来并可以展现历史记录。

时间预估

PSP 2.1 Personal Software Process Stages 预估耗时(min) 实际耗时(min)
Planning 计划 60
Estimate 估计这个任务需要多少时间 60
Development 开发 2400
Analysis 需求分析(包括学习新技术) 360
Design Spec 生成设计文档 120
Design Review 设计复审(和同事审核审计文档) 60
Coding Standard 代码规范(为目前的开发制定合适的规范) 60
Design 具体设计 360
Coding 具体编码 600
Code Review 代码复审 480
Test 测试(自我测试,修改代码,提交修改) 360
Reporting 报告 420
Test Report 测试报告 120
Size Measurement 计算工作量 120
Postmortem & Process Improvement Plan 事后总结,并提出过程改进计划 180
合计 2880

需求分析

模块划分

程序要同时支持整数和真分数的四则运算,由于分数无法直接运算,因此单独进行处理。

  • 整数题目
    包含第一阶段的整数题目生成以及第二阶段的乘方运算。
  • 分数题目
    包括第一阶段分数的四则运算和题目的生成。

分工为:我完成分数题目部分,队友完成整数部分和界面部分。

队友传送门-> 闪bo

设计实现

定义class PF真分数类,包含:

  • int mother 分母
  • int son 分子
  • PF() 随机生成分母分子值的构造函数
  • PF(int _mother, int _son) 指定分母分子值的构造函数
  • Display() 显示函数(将分数显示在命令行中方便调试)
  • ConvertString() 真分数转字符串的转换函数(方便存储到字符串中)
  • GetRandomSeed() 随机数种子函数(用于随机生成分母分子值)
  • MaxComDivisor(int x, int y) 最大公因子函数(用于约分化简)
  • operator +-*/(PF p1, PF p2) 重载四种运算符(用于分数分四则运算)

定义class Problem题目类,包含:

  • string op1 op2 op3 3个运算符
  • PF p1 p2 p3 p4 4个运算数
  • Problem() 生成一道题目的构造函数
  • NextProblem() 生成下一道题目的函数
  • CalValue() 表达式求值函数(计算当前题目答案)
  • ConvertString() 真分数转字符串的转换函数(方便存储到字符串中)
  • GetRandomSeed() 随机数种子函数(用于随机生成分母分子值)
  • MaxComDivisor(int x, int y) 最大公因子函数(用于约分化简)
  • operator +-*/(PF p1, PF p2) 重载四种运算符(用于分数分四则运算)

关键代码

1.随机生成真分数(构造函数)

 public PF()
        {
        	Random rand = new Random(GetRandomSeed());
        	mother = rand.Next(3,10);
            son = rand.Next(1,2);
            int maxDivisor = MaxComDivisor(Math.Abs(mother), Math.Abs(son));
            mother /= maxDivisor;
            son /= maxDivisor;
        }

2.重载四个运算符

public static PF operator+ (PF p1,PF p2)  
        {  
           int mother = p1.mother * p2.mother;
           int son1 = p1.son * p2.mother;
           int son2 = p2.son * p1.mother;
           int son = son1 + son2;
           PF sum = new PF(mother, son);
           return sum;
        }

        public static PF operator- (PF p1,PF p2)  
        {  
           int mother = p1.mother * p2.mother;
           int son1 = p1.son * p2.mother;
           int son2 = p2.son * p1.mother;
           int son = son1 - son2;
           PF result = new PF(mother, son);
           return result;
        }

        public static PF operator* (PF p1,PF p2)  
        {  
           int mother = p1.mother * p2.mother;
           int son = p1.son * p2.son;
           PF result = new PF(mother, son);
           return result;
        }

        public static PF operator/ (PF p1,PF p2)  
        {  
           int mother = p1.mother * p2.son;
           int son = p1.son * p2.mother;
           PF result = new PF(mother, son);
           return result;
        }

3.出题

public Problem()
        {
            string[] op = new string[]{"+", "-", "*", "/", "-", "+"};
            Random rand = new Random(GetRandomSeed());
            op1 = op[rand.Next(6)];
            op2 = op[rand.Next(6)];
            op3 = op[rand.Next(6)];
            p1 = new PF();
            int baseMother = rand.Next(2,5)*p1.mother;
            p2 = new PF(baseMother,rand.Next(1,baseMother));
            baseMother = rand.Next(2,5)*p1.mother;
            p3 = new PF(baseMother,rand.Next(1,baseMother));
            baseMother = rand.Next(2,5)*p1.mother;
            p4 = new PF(baseMother,rand.Next(1,baseMother));
            problem = p1.ConvertString() + " " + op1 + " " + p2.ConvertString() + " " + op2 + " " + p3.ConvertString() + " " + op3 + " " + p4.ConvertString();
        }

4.转化为逆波兰表达式并求值

public PF CalValue()
        {
            string[] symbols = {"1", op1, "2", op2, "3", op3, "4"};
            Stack<string> operaStack = new Stack<string>();
            Queue<string> valueQueue = new Queue<string>();
            Stack<PF> numStack = new Stack<PF>();
            for (int i = 0; i < symbols.Length; i++)
            {
                if (IsInt(symbols[i]))
                {
                    valueQueue.Enqueue(symbols[i]);
                }
                else if (IsOperator(symbols[i]))
                {
                    while (operaStack.Count != 0 && GetOpLevel(operaStack.Peek())>=GetOpLevel(symbols[i]))
                    {
                        valueQueue.Enqueue(operaStack.Pop());
                    }
                    operaStack.Push(symbols[i]);
                }
            }            
            while (operaStack.Count != 0)
            {
                valueQueue.Enqueue(operaStack.Pop());
            }
            while(valueQueue.Count != 0)
            {
                if(IsInt(valueQueue.Peek()))
                {
                    switch (valueQueue.Peek())
                    {
                        case "1":
                            numStack.Push(p1);
                            break;
                        case "2":
                            numStack.Push(p2);
                            break;
                        case "3":
                            numStack.Push(p3);
                            break;
                        case "4":
                            numStack.Push(p4);
                            break;
                        default:
                            break;
                    }  
                    valueQueue.Dequeue();
                    // numStack.Peek().Display();
                    // Console.WriteLine(" ");
                }
                else
                {
                    string operation = valueQueue.Dequeue();
                    numStack.Push(GetResult(operation, numStack.Pop(), numStack.Pop()));
                }   
            }
            PF result = numStack.Pop();
            return result;
        }

时间统计

PSP 2.1 Personal Software Process Stages 预估耗时(min) 实际耗时(min)
Planning 计划 60 60
Estimate 估计这个任务需要多少时间 60 60
Development 开发 2400 2130
Analysis 需求分析(包括学习新技术) 360 300
Design Spec 生成设计文档 120 120
Design Review 设计复审(和同事审核审计文档) 60 60
Coding Standard 代码规范(为目前的开发制定合适的规范) 60 30
Design 具体设计 360 300
Coding 具体编码 600 720
Code Review 代码复审 480 360
Test 测试(自我测试,修改代码,提交修改) 360 240
Reporting 报告 420 270
Test Report 测试报告 120 90
Size Measurement 计算工作量 120 90
Postmortem & Process Improvement Plan 事后总结,并提出过程改进计划 180 120
合计 2880 2460

你可能感兴趣的:(【软件工程基础结队项目】四则运算题目生成)