Git地址https://github.com/Kowaine
Git用户名Kowaine
学号后五位62127
博客地址https://www.cnblogs.com/Kowaine/
作业链接https://www.cnblogs.com/harry240/p/11515697.html



目录


VS环境配置

由于环境早已配置好,此处只作简要说明


打开vs安装程序后,勾选C#开发所需组件,一路点击继续,即能安装完成(可能需要重启)


安装完成后,创建一个helloworld程序,确定能否运行



正常运行





克隆Github项目


在cmd或者gitbash中输入克隆命令(cmd中需要配置环境变量)




克隆完成后的文件夹



至此,项目的克隆就完成了
然后,进入克隆的项目文件夹中,新建一个以自己github用户名相同的文件夹



之后,在VS中以这个新建的文件夹为根目录创建一个控制台项目




至此,项目开发的准备就完成了




编写基础程序


需求整理

1.接收一个整数参数 n,随机产生 n 道加减乘除(分别使用符号+-*/来表示)练习题。
2.数字取值范围[0, 100],运算符2个或3个。
3.运算结果为整数
4 结果以“subject.txt”的文件格式输出

设计思路

设计的难点主要在于结果必须为整数这一点上,很容易发现的是,只有除法可能产生小数。因此,整体设计的重点就在于如何让除法不产生小数结果上。
经过思考,我发现,由于除号和乘号在没有括号的四则运算中有着最高的优先级,而其他运算又不会产生小数,所以只要保证除号前后两个数的运算不产生小数即可。


最终,具体设计思路如下:
1.生成两到三个运算符。
2.从后往前生成运算数,除号前的数用除号后的数乘以一个随机数得到
3.运用堆栈计算并返回完整字符串
4.重复以上步骤n次后,将全部计算式写入文件

具体代码(初版)


class Program
{
    static void Main(string[] args)
    {
        Console.Write("Please input the number of problems: ");
        int n = int.Parse(Console.ReadLine());
        string[] problems = new string[n];
        for (int i = 0; i < n; ++i)
        {
            //生成一组数据
            ArrayList data = generateData();
            int result = Calculate((Stack)(((Stack)data[0]).Clone()), (Stack)(((Stack)data[1]).Clone()));
            if (result < 0)
            {
                i--;
                continue;
            }
            problems[i] = toString((Stack)(((Stack)data[0]).Clone()), (Stack)(((Stack)data[1]).Clone()), result);
        }
        writeToFile(problems);

    }



    /// <summary>
    /// 操作符基类
    /// </summary>
    public interface Operator
    {
        //计算
        int doCal(int x, int y);

        string toString();

        int getPriority();
    }

    public class Add : Operator
    {
        private int priority;

        public Add()
        {
            priority = 0;
        }


        public int doCal(int x, int y)
        {
            return x + y;
        }

        public string toString()
        {
            return "+";
        }

        public int getPriority()
        {
            return priority;
        }

    }

    public class Sub : Operator
    {
        private int priority;

        public Sub()
        {
            priority = 1;
        }


        public int doCal(int x, int y)
        {
            return x - y;
        }

        public string toString()
        {
            return "-";
        }

        public int getPriority()
        {
            return priority;
        }
    }

    public class Mul : Operator
    {
        private int priority;

        public Mul()
        {
            priority = 2;
        }


        public int doCal(int x, int y)
        {
            return x * y;
        }

        public  string toString()
        {
            return "*";
        }

        public int getPriority()
        {
            return priority;
        }
    }

    public class Div : Operator
    {
        private int priority;

        public Div()
        {
            priority = 3;
        }


        public  int doCal(int x, int y)
        {
            return x / y;
        }

        public  string toString()
        {
            return "/";
        }

        public int getPriority()
        {
            return priority;
        }
    }

    /// <summary>
    /// 生成运算符的工厂类
    /// </summary>
    public class OperatorFactory
    {
        private Random random = new Random();

        /// <summary>
        /// 生成一个随机的操作符类
        /// </summary>
        /// <returns>生成的操作符类</returns>
        public Operator randOprator()
        {
            int index = random.Next(0, 4);
            switch(index)
            {
                case 0:
                    return new Add();
                case 1:
                    return new Sub();
                case 2:
                    return new Mul();
                case 3:
                    return new Div();
                default:
                    return null;
            }
        }
    }

    /// <summary>
    /// 产生随机数的工厂类
    /// </summary>
    public class NumberFactory
    {
        private Random random = new Random();


        public int randNumber(int minNum, int maxNum)
        {
            return random.Next(minNum, maxNum + 1);
        }
    }

    /// <summary>
    /// 计算
    /// </summary>
    /// <param name="ops">操作符的反向栈</param>
    /// <param name="nums">操作数的反向栈</param>
    /// <returns>最终结果</returns>
    public static int Calculate(Stack ops, Stack nums)
    {
        // 生成两个栈供使用
        Stack opStack = new Stack();
        Stack numStack = new Stack();

        // 开始入栈计算
        numStack.Push(nums.Pop());
        while (nums.Count != 0)
        {
            numStack.Push(nums.Pop());
            Operator thisOp = (Operator)ops.Pop();
            if (ops.Count != 0)
            {
                Operator nextOp = (Operator)ops.Peek();
                // 比较优先级,若当前运算符优先级大于等于下一个运算符,则先运算
                if (thisOp.getPriority() >= nextOp.getPriority())
                {
                    int x2 = (int)numStack.Pop();
                    int x1 = (int)numStack.Pop();
                    int result = thisOp.doCal(x1, x2);
                    numStack.Push(result);
                }
                else
                {
                    opStack.Push(thisOp);
                }
            }
            else
            {
                opStack.Push(thisOp);
            }
        }
        // 确保运算完全完成
        while (opStack.Count != 0)
        {
            int x2 = (int)numStack.Pop();
            int x1 = (int)numStack.Pop();
            numStack.Push(((Operator)opStack.Pop()).doCal(x1, x2));
        }

        return (int)numStack.Pop();
    }

    /// <summary>
    /// 生成一组计算所需数据
    /// </summary>
    /// <returns>ArrayList(操作符反向栈, 操作数反向栈)</returns>
    public static ArrayList generateData()
    {
        // 创建工厂
        OperatorFactory operatorFactory = new OperatorFactory();
        NumberFactory numberFactory = new NumberFactory();

        // 操作符数量
        Random random = new Random();
        // 生成操作符
        int opCount = random.Next(2, 4);
        ArrayList opList = new ArrayList();
        for (int i = 0; i < opCount; ++i)
        {
            opList.Add(operatorFactory.randOprator());
        }
        opList.Reverse();

        // 根据操作符数量生成操作数
        ArrayList numList = new ArrayList();
        numList.Add(numberFactory.randNumber(1, 99));
        int divCount = 0;
        for (int i = 0; i < opCount; ++i)
        {
            string nextOp;
            if (i == opCount - 1)
            {
                nextOp = "";
            }
            else
            {
                nextOp = ((Operator)opList[i + 1]).toString();
            }
            if (((Operator)opList[i]).toString() == "/" && nextOp != "/")
            {
                // 在连续除号的最后一个生成数字
                int[] numArray = new int[divCount + 1];
                numArray[divCount] = 101;
                while (numArray[divCount] > 99)
                {
                    numList[i - divCount] = numberFactory.randNumber(1, 10);
                    int lastNum = (int)numList[i-divCount];
                    int fistNum = lastNum;
                    //(100 / lastNum) >= 3 ? (100 / lastNum) : 3
                    int n = numberFactory.randNumber(2, (99 / lastNum) >= 3 ? (99 / lastNum) : 3);
                    numArray[0] = n * lastNum;
                    for (int j = 1; j < divCount + 1; ++j)
                    {
                        lastNum = numArray[j - 1];
                        n = numberFactory.randNumber(2, (99 / lastNum) >= 3 ? (99 / lastNum) : 3);
                        numArray[j] = n * lastNum * fistNum;
                    }
                }
                for (int j = 0; j < divCount + 1; ++j)
                {
                    numList.Add(numArray[j]);
                }
                divCount = 0;
            }
            else if (((Operator)opList[i]).toString() == "/" && nextOp == "/")
            {
                divCount++;
            }
            else
            {
                numList.Add(numberFactory.randNumber(0, 99));
            }
        }

        // 转化为栈
        Stack opStack = new Stack();
        for (int i = 0; i < opList.Count; ++i)
        {
            opStack.Push(opList[i]);
        }
        Stack numStack = new Stack();
        for (int i = 0; i < numList.Count; ++i)
        {
            numStack.Push(numList[i]);
        }

        // 转化为一个List
        ArrayList data = new ArrayList();
        data.Add(opStack);
        data.Add(numStack);

        return data;
    }

    public static string toString(Stack opStack, Stack numStack, int result)
    {
        string resultString = "";
        resultString += ((int)numStack.Pop()).ToString();
        int count = opStack.Count;
        for (int i = 0; i < count; i++)
        {
            resultString += ((Operator)opStack.Pop()).toString();
            resultString += ((int)numStack.Pop()).ToString();
        }
        resultString += "=" + result.ToString();
        return resultString;
    }

    public static void writeToFile(string[] problems)
    {
        string path = "subject.txt";
        if (File.Exists(path))
        {
            File.Delete(path);
        }
        StreamWriter writer = new StreamWriter(path, true);
        for (int i=0; i < problems.Length; ++i)
        {
            // 测试输出
            //Console.WriteLine(problems[i] + "\n");

            writer.WriteLine(problems[i]);

        }
        writer.Close();
        writer.Dispose();
    }
}




先用 "git add ."追踪新增的文件。然后用"git commit -am '代码初版' "提交代码



使用"git status" 确定已经提交的数据,最后"git push" 提交到github(若电脑中安装了github桌面版,会跳出登录界面)



登录到github,确认代码已经提交





单元测试的编写


首先,右键要测试的类或方法创建单元测试



编写单元测试



测试通过



本次实验主要目的在于工具的使用,故不在测试设计上多费功夫




VS调试的基本操作


断点


点击每行代码前可以添加断点,调试过程中程序将运行到此处暂停

调试


F11逐语句调试,F12逐过程调试,前者会进入函数调用单步执行,后者不会进入函数

变量监视


下方可以看到局部变量的变化,也可以右键自己添加监视






回归测试


回归测试,指每次代码发生修改后,将代码进行过的单元测试再次运行,保证每次修改没有影响程序的稳定性。
此行为的效果主要体现在代码中,不便说明,不多赘述。




效能分析


分析-性能探查器,勾选需要的项目后,点击开始运行分析



先更改部分代码以便测试,循环次数设置为1,000,000次



分析中



分析完成



更具体的数据






发起分支合并请求


点击NewPullRequest



点击CreatePullRequest



输入请求的title



至此,提交完成。




感想


本次实验,说难也难,说简单也简单。
简单在于,实验中涉及到的工具,原本就是我经常使用的东西,所以在操作上并没有什么问题。
难点在于两个地方。
第一,自己对C#这门语言并没有系统性地学习过,对程序结构、语法都没有什么了解和认识,在程序的编写上不得不边查找资料边编写,浪费了不少的时间。
第二,个人对于算法并没有多少研究,虽然程序大体实现并没有什么问题,在一些小细节上却琢磨了不少。某些需求虽然自己有想出解决方案,但是由于认为效率实在是太低、而且缺乏简洁美,最终还是没有加上去。
总的来说,这次实验让我充分认识到了自己能力上的不足,还要更多地学习、更多地练习,才能成为一名合格的程序员。

02-13 07:17