熟练使用工具

|
:-😐:-:
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程序,确定能否运行 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190918172645989-1631456561.png)
正常运行 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190918172807079-237255116.png)

克隆Github项目

在cmd或者gitbash中输入克隆命令(cmd中需要配置环境变量)
git clone https://github.com/Kowaine/AchaoCalculator.git
![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190918173928868-843194693.png)
克隆完成后的文件夹 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190918174111138-229839435.png)
至此,项目的克隆就完成了 然后,进入克隆的项目文件夹中,新建一个以自己github用户名相同的文件夹 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190918175457782-1650131926.png)
之后,在VS中以这个新建的文件夹为根目录创建一个控制台项目 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190918180731869-972801919.png) ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190918180952232-1077510375.png)
至此,项目开发的准备就完成了

编写基础程序

需求整理

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);

}

///


/// 操作符基类
///

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;
}
}

///


/// 生成运算符的工厂类
///

public class OperatorFactory
{
private Random random = new Random();

///


/// 生成一个随机的操作符类
///

/// 生成的操作符类
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;
}
}
}

///


/// 产生随机数的工厂类
///

public class NumberFactory
{
private Random random = new Random();

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

///


/// 计算
///

/// 操作符的反向栈
/// 操作数的反向栈
/// 最终结果
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();
}

///


/// 生成一组计算所需数据
///

/// ArrayList(操作符反向栈, 操作数反向栈)
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,确认代码已经提交




单元测试的编写

首先,右键要测试的类或方法创建单元测试 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919151935816-1054339738.png)
编写单元测试 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919153157536-834606222.png)
测试通过 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919153222926-1917723516.png)
本次实验主要目的在于工具的使用,故不在测试设计上多费功夫

VS调试的基本操作

断点

点击每行代码前可以添加断点,调试过程中程序将运行到此处暂停 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919154759945-261214416.png)

调试

F11逐语句调试,F12逐过程调试,前者会进入函数调用单步执行,后者不会进入函数 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919155106072-757641251.png)

变量监视

下方可以看到局部变量的变化,也可以右键自己添加监视 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919155211803-327853263.png) ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919155259068-959601663.png)

回归测试

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

效能分析

分析-性能探查器,勾选需要的项目后,点击开始运行分析 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919160802896-951000972.png)
先更改部分代码以便测试,循环次数设置为1,000,000次 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919165715635-173676591.png)
分析中 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919193435133-1772775817.png)
分析完成 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919193458632-248719970.png)
更具体的数据 ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919193525095-1542409678.png) ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919193537015-2137032351.png)

发起分支合并请求

点击NewPullRequest ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919170907234-553604462.png)
点击CreatePullRequest ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919190108776-623859300.png)
输入请求的title ![](https://img2018.cnblogs.com/blog/1784814/201909/1784814-20190919190506740-163887024.png)
至此,提交完成。

感想

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