BIT软件工程结对项目——四则运算题目生成

BIT软件工程结对项目——四则运算题目生成

目录:

(点击可页内跳转)
1. 项目地址
2. PSP表格
3. 解题思路描述
———3.1项目分工
———3.2需求分析
———3.3类的组织
4.分工部分设计实现(不包括队友部分)
5.单元测试
6.心得感悟

1.项目地址

团队项目已经上传GitHub。
控制台项目地址:https://github.com/Archie7777/Arithmetic-Problems
GUI版本:https://github.com/Archie7777/Shitty-Game
队友博客网址:https://blog.csdn.net/weixin_43407283/article/details/86478548

2.PSP表格

PSP Personal Software Process Stages 计划用时(min) 实际用时(min)
PLANNING 计划 - -
----estimate 估计这个任务需要多长时间 10 10
DEVELOPMENT 开发 - -
----analysis 需求分析(包括学习新技术) 120 200
----design spec 生成设计文档 60 80
----design review 设计复审(和同事审核设计文档) 30 20
----coding standard 代码规范(为目前的开发制定合适的规范) 10 10
----design 具体设计 20 60
----coding 具体编码 1200 1800
----code review 代码复审 300 300
----test 测试(自我测试,修改代码,提交修改) 120 200
REPORTING 报告
test report 测试报告 20 60
size measurement 计算工作量 10 10
postmortem & process improvement plan 事后总结,并提出过程改进计划 180 100
合计 2080 2850

3.解题思路描述

因为需要实现的功能较少,且不是特别复杂,在设计初期决定采用瀑布模型进行开发,在编程实现过程中一有进展就签入,保证了工程逐步进展。

3.1项目分工

在项目的设计初期,我们将项目划分成了2个部分,运算表达式生成部分(也就是工程主体)和GUI部分。最后决定由另一名同学完成GUI和主要数据结构的设计和实现,我写参数传入,主函数,运算表达式生成的实现部分。

因为两个人都从来没用过C#,所以我和队友打算用C#在VS下完成项目开发,顺便还能学习一门新的语言。

3.2需求分析

我们组先仔细研读了项目的题目,提取出其中的关键信息如下:

1.第一阶段:
a)生成1000道不重复的题目写入文件中
b)实现表达式求值功能
c)支持真分数的四则运算
d)程序接受用户输入的对错并完成判断,给出总共 对/错 数量

2.第二阶段:
添加乘方运算

3.第三阶段:
实现图形用户界面GUI

3.3类的组织

我们将程序的主题分为6个类:
ArgumentParse:参数分析,用来分析命令行输入,向程序内部传递命令行参数。
Component:表达式构件类(抽象类),目前没有用到。
Expression:表达式类。
Number:操作数类,操作数的相关操作都在其中,允许真分数运算。把整数也看做是分数,只是整数的分母默认为1。
Operation:运算基本操作类,并将结果输出。
Program:主函数类,程序的执行入口,用户可以选择不同难度级别。

4.分工部分设计实现(不包括队友部分)

在具体实现过程中,我承担了Program.cs(主程序类)、ArgumentParser.cs(参数分析类)和Expression.cs(表达式操作类)三个类的编写。

1.主程序Program.cs:
首先说一下主程序Program.cs的执行顺序:
BIT软件工程结对项目——四则运算题目生成_第1张图片
当从命令行传入参数时,先用传入的参数初始化一个ArgumentParser型对象。然后通过这个ArgumentParser型对象方法接口的调用,对参数进行判断,使完成相关操作。

-g输入时:
1)如果选择输入的参数中含有-c或-d表示有乘方操作。
2)用户可以自行选择难度,如果输入的参数中有-h,就生成比较难的题目。

-p输入时:
只有用户输入stop,才会跳出循环,游戏结束,程序终止。

主函数代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace mathproblem
{
    class Program
    {
        static void Main(string[] args)
        {
            ArgumentParser argument = new ArgumentParser(args);
            if (argument.GetLength() == 0)
            {
                System.Console.WriteLine("Please enter an argument.");
                System.Console.WriteLine("Usage:\n" +
                    "generate 1000 problems: -g absolute_path_of_output_file\n" +
                    "play games: -p num_of_problem_you_want_to_play\n" +
                    "solve problems: -s absolute_path_of_problem_file");
                return;
            }

            if (argument.Get(1) == "-g")
            {
                if (argument.Has("-d") && argument.Has("-c"))
                    Console.WriteLine("Please input correct argument");
                else if (argument.Has("-d"))
                {
                    Expression.SetPrintType(Expression.PowerOpPrintType.doubleStar);
                    Expression.SetPowerOp(Expression.IsPowerOp.yes);
                }
                else if (argument.Has("-c"))
                {
                    Expression.SetPrintType(Expression.PowerOpPrintType.caret);
                    Expression.SetPowerOp(Expression.IsPowerOp.yes);
                }
                else
                    Expression.SetPowerOp(Expression.IsPowerOp.no);
                if (argument.Has("-h"))
                    Expression.SetDifficulty(Expression.Difficulty.hard);
                using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"./Expressions.txt", false))
                {
                    for (int i = 0; i < argument.ToInt(2); i++)
                        Generate(file);
                }
                return;
            }

            if (argument.Get(1) == "-p")
            {
                int correctNum = 0;
                int wrongNum = 0;
                while (true)
                {
                    Expression ep = new Expression();
                    while (ep.IsInvalid())
                    {
                        ep = new Expression();
                    }
                    ep.PrintExpression();
                    string answer = Console.ReadLine();
                    if (answer == "stop")
                    {
                        Console.WriteLine("正确数量:{0} 错误数量:{1}", correctNum, wrongNum);
                        break;
                    }
                    if (answer == ep.GetAnswerString())
                        correctNum++;
                    else wrongNum++;
                }
                return;
            }

            System.Console.WriteLine("Please enter correct argument.");
            return;
        }
        static void Generate(System.IO.StreamWriter file)
        {
            Expression ep = new Expression();
            while (ep.IsInvalid())
            {
                ep = new Expression();
            }
            ep.PrintExpressionWithAnswerToFile(file);
        }
    }
}

2. 参数分析类ArgumentParser.cs:
初始化之后,内部的各种方法基本上就是一些直接get成员变量值的操作了。

参数分析类ArgumentParser代码如下:

class ArgumentParser
    {
        public ArgumentParser(string[] args)
        {
            arguments = new string[args.Length];
            for (int i = 0; i < args.Length; i++)
                arguments[i] = args[i];
        }
        public bool Has(string arg)
        {
            for (int i = 0; i < arguments.Length; i++)
                if (arg == arguments[i]) return true;
            return false;
        }
        public bool NotHas(string arg)
        {
            return !Has(arg);
        }
        public int GetLength()
        {
            return arguments.Length;
        }
        public string Get(int num)
        {
            return arguments[num - 1];
        }
        public int ToInt(int num)
        {
            return Convert.ToInt32(arguments[num - 1]);
        }
        private string[] arguments;
    }

3.表达式分析类Expression.cs:
可以根据用户选择的难度生成不同的表达式树:
如果是用户在命令行没有输入-h,默认就是生成简单树;如果输入了-h就是困难模式,生成困难树。

需要注意的异常处理:一旦发生除零现象,表示表达式无效,需要重新生成

简单表达式就是“A 操作符 B”,表达式对应的二叉树是简单树(只有2层)。左孩子存放左操作数A,右孩子存放右操作数B,根节点存放操作符
生成表达式简单树的过程:
1)左孩子节点随机选择0-21范围内的操作数,
2)根节点根据用户选择是否有乘方操作来选择。
——2.1如果用户选择无乘方操作,就在{+,-,* ,/}四种运算中随机选择
——2.2 else,如果用户选择有乘方操作,就在{+,-,* ,/}五种运算中随机选择
3)右孩子节点根据根节点的选择结果来定
如果根节点是普通运算符,那么右结点的值就在0-21之间随机选择
如果根节点是乘方运算符,那么右结点的值就是0-3之间随机选择
4)简单树生成完毕

如果需要输出,就用ChangeToString()将表达式树转化为字符串,存入string变量expression中,等待输出。

生成困难树的步骤是一个递归过程,困难树从根节点开始向下逐层生成,第一层根节点必须是运算符,第二层、第三层可以是数字也可以是运算符,第四层(如果有的话)则必须是数字:
1)如果当前是根节点(第1层),就在这个节点处生成运算符
2)如果还没有达到第8个结点(当前处于第2层或第3层)
——2.1)有1/2的概率选择在此处生成运算符
——2.2)否则,在此处继续延伸(生成左子树,右子树),直到达到第8个结点
3)树中节点数已经达到8及以上(到达第4层),就停止向下继续延伸,只生成当前节点的数值即可。

5.单元测试

测试主要针对主函数模块和表达式生成模块进行,在单元测试过程中,均检查模块在执行过程中所有使用到的变量是否符合预定值,以及输出是否规范。

用例目的 输入 期待输出 实际输出
零参数输入 mathproblem.exe 参数输入提示字符 参数输入提示字符
无效命令行输入 mathproblem.exe -g -c -d “Please input correct argument” “Please input correct argument”
正常输入 mathproblem.exe -g -c 包含1000个带答案的简单表达式的文件 包含1000个带答案的简单表达式的文件
测试困难模式 mathproblem.exe -g -h 包含1000个带答案的困难表达式的文件 包含1000个带答案的困难表达式的文件

6. 心得体会

这次结对项目的经历让我深切体会到团队协作的重要性。软件开发在于团队小而精,在只有两个人的团队中,交流方便,思路清晰,开发效率相当高。可见软件开发并不是人越多,干的越快,效果越好。

但是在两个人相互配合的过程中也遇到了一些问题,两个人的代码风格差别很大,所以必须依照一些编程规范才能不影响双方的配合。

小组成员在配合的过程中,也遇到了下面这些由于编程经验不足造成的问题:
1)变量命名不规范:命名过于精简,造成语意不明
2)if语句嵌套使用过多,会影响程序的可读性,使程序逻辑混乱。
3)代码注释过少会给读者带来很大困难。

你可能感兴趣的:(BIT软件工程结对项目——四则运算题目生成)