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#这门语言并没有系统性地学习过,对程序结构、语法都没有什么了解和认识,在程序的编写上不得不边查找资料边编写,浪费了不少的时间。
第二,个人对于算法并没有多少研究,虽然程序大体实现并没有什么问题,在一些小细节上却琢磨了不少。某些需求虽然自己有想出解决方案,但是由于认为效率实在是太低、而且缺乏简洁美,最终还是没有加上去。
总的来说,这次实验让我充分认识到了自己能力上的不足,还要更多地学习、更多地练习,才能成为一名合格的程序员。