用户需求:
程序能接收用户输入的整数答案,并判断对错
程序结束时,统计出答对、答错的题目数量。
补充说明:0——10的整数是随机生成的
用户可以选择四则运算中的一种
用户可以结束程序的运行,并显示统计结果。
在此基础上,做增量开发。
增量内容:
(1)处理用户的错误输入,比如输入字母或符号等,处理除法运算中分母为0的情况,处理结果为负数的情况,保证是小学水平不出现负数,比如不能出现5-8=-3这种情况;
(2)用户可以设定倒计时;
(3)用户可以设定随机整数的范围和题目数量;
(4)用户可以选择哪种计算类型,比如加减乘除,或可选择软件随机生成四则运算中的一种;
(5)用户可以选择随机生成的题目中是否带有小括号,比如(2+3)*5,如果是gui程序,添加这个功能可以用复选框实现。
(6)保证生成过的题目不再重复出现。
设计思路:
我的想法是,先生成两个数运算的表达式,就和上次的一样。先让两个数进行加减计算,得到一个结果,之后再与第三个数进行乘除运算。如果要是这样做的话就要让它产生两个运算符,那就会有先算乘除后算加减了。就不能达到(2+3)*5这种先加减后乘除的运算了。并且也不能一次就生成一个三个数运算的表达式。所以就把这种想法舍弃了。
因此呢,我们就把第一个和第二个文本框合并成了一个文本框,让它来存一个运算表达式。不管是生成两个数亦或是三个数的表达式都可以,最后只需要对这个字符串表达式进行计算就可以了。这才只是生成一个式子。难点在于对这个式子进行计算,要把这个式子取出来,用到队列,把它的每一个字符都取出来放到了栈里之后再进行计算。
代码实现:
Form1.cs
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace _Random 11 { 12 public partial class Form1 : Form 13 { 14 public Form1() 15 { 16 InitializeComponent(); 17 } 18 public static int select = 0; 19 public static int Count = 0; 20 private int t = 60; 21 public static int right = 0; 22 23 private void button1_Click(object sender, EventArgs e) 24 { 25 label2.Text = t.ToString(); 26 timer1.Enabled = true; 27 timer1.Interval = 1000; 28 timer1.Start(); 29 } 30 31 private void RDN() 32 { 33 Random ran=new Random(); 34 int n1,n2; 35 if (textBox4.Text==""&&textBox5.Text=="") 36 { 37 MessageBox.Show("请输入取值范围!"); 38 return; 39 } 40 if (checkBox1.Checked == true) 41 select = 1; 42 for (int i = 0; i < int.Parse(textBox6.Text); i++) 43 { 44 textBox1.Clear(); 45 switch (select) 46 { 47 case 1: 48 { 49 n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 50 n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 51 int n3 = ran.Next(1, int.Parse(textBox5.Text)); 52 textBox1.Text += "("; 53 if (n1 < 0) textBox1.Text += "(" + n1 + ")"; 54 else textBox1.Text += n1; 55 if (ran.Next(0, 2) == 1) textBox1.Text += "+"; 56 else textBox1.Text += "-"; 57 if (n2 < 0) textBox1.Text += "(" + n2 + ")"; 58 else textBox1.Text += n2 + ")"; 59 if (ran.Next(0, 2) == 1) textBox1.Text += "*"+n3; 60 else textBox1.Text += "/" + n3; 61 break; 62 } 63 } 64 65 textBox3.Text = ""; 66 } 67 } 68 69 private void RandomNumjia() 70 { 71 textBox1.Clear(); 72 textBox3.Clear(); 73 if (textBox4.Text == "" && textBox5.Text == "") 74 { 75 MessageBox.Show("请输入取值范围!"); 76 return; 77 } 78 79 Random ran = new Random(); 80 int n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 81 int n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 82 if (n1 < 0) textBox1.Text += "(" + n1 + ")"; 83 else textBox1.Text += n1; 84 textBox1.Text += "+"; 85 if (n2 < 0) textBox1.Text += "(" + n2 + ")"; 86 else textBox1.Text += n2; 87 } 88 89 private void RandomNumjian() 90 { 91 textBox1.Clear(); 92 textBox3.Clear(); 93 if (textBox4.Text == "" && textBox5.Text == "") 94 { 95 MessageBox.Show("请输入取值范围!"); 96 return; 97 } 98 99 Random ran = new Random(); 100 int n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 101 int n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 102 if (n1 < 0) textBox1.Text += "(" + n1 + ")"; 103 else textBox1.Text += n1; 104 textBox1.Text += "-"; 105 if (n2 < 0) textBox1.Text += "(" + n2 + ")"; 106 else textBox1.Text += n2; 107 } 108 109 private void RandomNumcheng() 110 { 111 textBox1.Clear(); 112 textBox3.Clear(); 113 if (textBox4.Text == "" && textBox5.Text == "") 114 { 115 MessageBox.Show("请输入取值范围!"); 116 return; 117 } 118 119 Random ran = new Random(); 120 int n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 121 int n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 122 if (n1 < 0) textBox1.Text += "(" + n1 + ")"; 123 else textBox1.Text += n1; 124 textBox1.Text += "*"; 125 if (n2 < 0) textBox1.Text += "(" + n2 + ")"; 126 else textBox1.Text += n2; 127 } 128 129 private void RandomNumchu() 130 { 131 textBox1.Clear(); 132 textBox3.Clear(); 133 if (textBox4.Text == "" && textBox5.Text == "") 134 { 135 MessageBox.Show("请输入取值范围!"); 136 return; 137 } 138 139 Random ran = new Random(); 140 int n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 141 int n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 142 if (n1 < 0) textBox1.Text += "(" + n1 + ")"; 143 else textBox1.Text += n1; 144 textBox1.Text += "/"; 145 if (n2 < 0) textBox1.Text += "(" + n2 + ")"; 146 else textBox1.Text += n2; 147 } 148 149 private void timer1_Tick(object sender, EventArgs e) 150 { 151 if (t <= 0) 152 { 153 timer1.Enabled = false; 154 textBox3.Enabled = false; 155 MessageBox.Show("时间到!"); 156 textBox3.Enabled = false; 157 Form2 frm2 = new Form2(); 158 frm2.ShowDialog(); 159 } 160 t = t - 1; 161 label2.Text = t.ToString(); 162 } 163 164 private void button2_Click(object sender, EventArgs e) 165 { 166 timer1.Stop(); 167 Form2 frm2 = new Form2(); 168 frm2.ShowDialog(); 169 } 170 171 private void button3_Click(object sender, EventArgs e) 172 { 173 RandomNumjia(); 174 } 175 176 private void button4_Click(object sender, EventArgs e) 177 { 178 RandomNumjian(); 179 } 180 181 private void button5_Click(object sender, EventArgs e) 182 { 183 RandomNumcheng(); 184 } 185 186 private void button6_Click(object sender, EventArgs e) 187 { 188 RandomNumchu(); 189 } 190 191 private void button7_Click(object sender, EventArgs e) 192 { 193 if (textBox4.Text == "" && textBox5.Text == "") 194 { 195 MessageBox.Show("请输入取值范围!"); 196 return; 197 } 198 else 199 { 200 for (int i = 0; i < int.Parse(textBox6.Text);i++) 201 { 202 RDN(); 203 } 204 } 205 } 206 207 private void textBox3_KeyDown(object sender, KeyEventArgs e) 208 { 209 string result = textBox1.Text; 210 211 if (Count == int.Parse(textBox6.Text)) 212 { 213 Form2 frm2 = new Form2(); 214 frm2.ShowDialog(); 215 } 216 217 if (e.KeyCode == Keys.Enter) 218 { 219 if (textBox3.Text == Calucate(result).ToString()) //直接调用Calucate这个方法计算result的值并与输入的值进行比较 220 { 221 right++; 222 Count++; 223 MessageBox.Show("回答正确!"); 224 } 225 226 else 227 { 228 MessageBox.Show("答题错误!"); 229 Count++; 230 string s = textBox1.Text; 231 if (s.Substring(textBox4.TextLength, 1) == "+") 232 { 233 RandomNumjia(); 234 } 235 else if (s.Substring(textBox4.TextLength, 1) == "-") 236 { 237 RandomNumjian(); 238 } 239 else if (s.Substring(textBox4.TextLength, 1) == "*") 240 { 241 RandomNumcheng(); 242 } 243 else if (s.Substring(textBox4.TextLength, 1) == "/") 244 { 245 RandomNumchu(); 246 } 247 else if (s.Length > 5) 248 { 249 RDN(); 250 } 251 } 252 253 string m = textBox1.Text; 254 if (m.Substring(textBox4.TextLength, 1) == "+") 255 { 256 RandomNumjia(); 257 } 258 else if (m.Substring(1, 1) == "-") 259 { 260 RandomNumjian(); 261 } 262 else if (m.Substring(1, 1) == "*") 263 { 264 RandomNumcheng(); 265 } 266 else if (m.Substring(1, 1) == "/") 267 { 268 RandomNumchu(); 269 } 270 else if (m.Length > 5) 271 { 272 RDN(); 273 } 274 } 275 } 276 277 static Dictionary<char, int> priorities = null; //优先级 278 279 static void Calculator() //添加了四种运算符以及四种运算符的优先级 280 { 281 priorities = new Dictionary<char, int>(); 282 priorities.Add('#', -1); 283 priorities.Add('+', 0); 284 priorities.Add('-', 0); 285 priorities.Add('*', 1); 286 priorities.Add('/', 1); 287 } 288 289 const string operators = "+-*/"; //运算符 290 static double Compute(double leftNum, double rightNum, char op) //这是一种方法,用来计算左右两个数的静态方法! 291 { 292 switch (op) 293 { 294 case '+': return leftNum + rightNum; 295 case '-': return leftNum - rightNum; 296 case '*': return leftNum * rightNum; 297 case '/': return leftNum / rightNum; 298 default: return 0; 299 } 300 } 301 302 static bool IsOperator(char op) //每次判断这个字符是否是运算符? 303 { 304 return operators.IndexOf(op) >= 0; 305 } 306 307 static bool IsAssoc(char op) //返回一个关联符号 308 { 309 return op == '+' || op == '-' || op == '*' || op == '/'; 310 } 311 312 static Queue<object> QueueSort (string expression) // 队列排序 313 { 314 Queue<object> result = new Queue<object>(); 315 Stack<char> operatorStack = new Stack<char>(); //运算符栈 316 operatorStack.Push('#'); 317 char top, cur, tempChar; //top栈顶,current最近的; 318 string tempNum; 319 for (int i = 0, j; i < expression.Length; ) //取出表达式 320 { 321 cur = expression[i++]; //取出表达式的每个字符赋给cur 322 top = operatorStack.Peek(); //栈顶元素赋给top此时为"#" 323 324 if (cur == '(') //将左括号压栈,此时栈顶元素为"(" 325 { 326 operatorStack.Push(cur); 327 } 328 else 329 { 330 if (IsOperator(cur)) //如果是运算符的话 331 { 332 while (IsOperator(top) && ((IsAssoc(cur) && priorities[cur] <= priorities[top])) || (!IsAssoc(cur) && priorities[cur] < priorities[top])) 333 { 334 result.Enqueue(operatorStack.Pop()); //如果元素为运算符并且优先级小于栈顶元素优先级,出栈 335 top = operatorStack.Peek(); //继续把栈顶元素赋给top 336 } 337 operatorStack.Push(cur); //把数字压栈 338 } 339 else if (cur == ')') //将右括号添加到结尾 340 { 341 while (operatorStack.Count > 0 && (tempChar = operatorStack.Pop()) != '(') 342 { 343 result.Enqueue(tempChar); 344 } 345 } 346 else 347 { 348 tempNum = "" + cur; 349 j = i; 350 while (j < expression.Length && (expression[j] == '.' || (expression[j] >= '0' && expression[j] <= '9'))) 351 { 352 tempNum += expression[j++]; 353 } 354 i = j; 355 result.Enqueue(tempNum); 356 } 357 } 358 } 359 while (operatorStack.Count > 0) 360 { 361 cur = operatorStack.Pop(); 362 if (cur == '#') continue; 363 if (operatorStack.Count > 0) 364 { 365 top = operatorStack.Peek(); 366 } 367 368 result.Enqueue(cur); 369 } 370 371 return result; 372 } 373 374 static double Calucate(string expression) 375 { 376 try 377 { 378 var rpn = QueueSort(expression); //rpn逆波兰表达式reverse polish notation 379 Stack<double> operandStack = new Stack<double>(); 380 double left, right; 381 object cur; 382 while (rpn.Count > 0) 383 { 384 cur = rpn.Dequeue(); //出列 385 if (cur is char) //如果cur为字符的话 386 { 387 right = operandStack.Pop(); //右边的数字出栈 388 left = operandStack.Pop(); //左边的数字出栈 389 operandStack.Push(Compute(left, right, (char)cur)); //此时调用compute方法 390 } 391 else 392 { 393 operandStack.Push(double.Parse(cur.ToString())); //是数字就压栈 394 } 395 } 396 return operandStack.Pop(); 397 } 398 catch 399 { 400 throw new Exception("表达式格式不正确!"); 401 } 402 } 403 404 } 405 }
代码编写过程:
Form2.cs
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace _Random 11 { 12 public partial class Form2 : Form 13 { 14 public Form2() 15 { 16 InitializeComponent(); 17 } 18 19 private void Form2_Load(object sender, EventArgs e) 20 { 21 textBox1.Text = Form1.Count.ToString(); 22 textBox2.Text = Form1.right.ToString(); 23 textBox3.Text = (Form1.Count - Form1.right).ToString(); 24 } 25 26 } 27 }
运行过程:
答题的时候给出取值范围,点击随机,程序会给出一个运算式,在等号后面的文本框
里面输入答案,这是会调用计算方法,并判断对错。
时间到了会给出测试结果。并提示时间到!
PSP耗时分析:
结对编程总结:
说明:我们一起做的是第五个增量。
这次结对编程依然是我的老搭档张宇,总的来说他是一个比较优秀的搭档,对我的辅导也非常尽心,通过这两次结对编程我学到了很多知识,也了解了自己以前不知道的逆波兰表达式,这些多亏我的搭档给我讲解。说实话看到老师发的作业增量一次比一次难我都有放弃的想法,多亏搭档坚持。当看到增量作业一点头绪都没后来我们在网上查了一些资料才有所了解。通过这次编程我明白了所谓的迭代,代码永远都敲不完,客户的每一次需求,我们都要进行一次代码修改,还要保证之前的功能依然可以实现,所以程序员真的不是一个轻松的活。但是我想只要努力就可以成功!