用户需求:
程序能接收用户输入的整数答案,并判断对错
程序结束时,统计出答对、答错的题目数量。
补充说明:0——10的整数是随机生成的
用户可以选择四则运算中的一种
用户可以结束程序的运行,并显示统计结果。
在此基础上,做增量开发。
增量内容: 1)处理用户的错误输入,比如输入字母或符号等,处理除法运算中分母为0的情况,处理结果为负数的情况,保证是小学水平不出现负数,比如不能出现5-8=-3这种情况;
2)用户可以设定倒计时;
3)用户可以设定随机整数的范围和题目数量;
4)用户可以选择哪种计算类型,比如加减乘除,或可选择软件随机生成四则运算中的一种;
5)用户可以选择随机生成的题目中是否带有小括号,比如(2+3)*5,如果是gui程序,添加这个功能可以用复选框实现。
6)保证生成过的题目不再重复出现。
设计思路:
因为是做增量啊,所以呢,我在看了5和6这两个增量之后,第一感觉是有点难度。这次和之前的运算不同的地方是:
对于5):加了小括号,这就要考虑优先级了,并且不能再用两个文本框来生成两个数字进行运算了。所以我考虑,把两个文本框合成一个文本框,这样的话就让它来存生成的一个式子,最后只对这个式子进行运算就OK了,这不但可以生成两个数的运算表达式,还可以生成三个数的运算表达式,就看你怎么定义出题方法了。当然了,想的是很简单,毕竟生成的是一个式子而并非两个数进行计算那么简单了。所以我继续分析,就对后面的这个式子进行研究啊。首先它是一个字符串表达式,而进行计算的话肯定不能用string类型的来进行计算。所以用队列把表达式里的每一个数取出来,并定义一个栈,让取出的元素放到栈里,这样的话只对栈里元素用逆波兰式进行计算就可以了。
1.对于实现 出题为一个字符串表达式,我用了如下这种方法:
n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text));
n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text));
int n3 = ran.Next(1, int.Parse(textBox5.Text));
textBox1.Text += "(";
if (n1 < 0) textBox1.Text += "(" + n1 + ")";
else textBox1.Text += n1;
if (ran.Next(0, 2) == 1) textBox1.Text += "+";
else textBox1.Text += "-";
if (n2 < 0) textBox1.Text += "(" + n2 + ")";
else textBox1.Text += n2 + ")";
if (ran.Next(0, 2) == 1) textBox1.Text += "*"+n3;
else textBox1.Text += "/" + n3;
break;
2.对于让它连续出一种运算的表达式,根据最小取值范围的字符长度,也就是截取第一个运算数后面的运算符来进行判断。如下:
if (s.Substring(textBox4.TextLength, 1) == "+")
{
RandomNumjia();
}
3.在准备工作做好之后就是进行计算了:
代码实现:
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 const string operators = "+-*/"; //运算符
278 static Dictionary<char, int> priorities = null; //优先级
279
280 static void Calculator() //添加了四种运算符以及四种运算符的优先级
281 {
282 priorities = new Dictionary<char, int>();
283 priorities.Add('#', -1);
284 priorities.Add('+', 0);
285 priorities.Add('-', 0);
286 priorities.Add('*', 1);
287 priorities.Add('/', 1);
288 }
289
290 static int Compute(int leftNum, int 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 int Calucate(string expression)
375 {
376 try
377 {
378 var rpn = QueueSort(expression); //rpn逆波兰表达式reverse polish notation
379 Stack<int> operandStack = new Stack<int>();
380 int 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(int.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耗时分析:
**
**结对编程总结:****
**
说明:我们一起做了1个增量
**5)用户可以选择随机生成的题目中是否带有小括号,比如(2+3)*5。
**
这次结对编程呢,还是和李燕燕一块的。总体来说,这次作业对我们来说确实有难度,因为那个逆波兰式我之前根本没有听说过,等到用到的时候才发现自己学到的知识真是太少了。我之前是觉得只要学到老师教的东西就行了,其实并不是,老师教的东西远远不够,想要使自己的编程能力提高,还是要靠我们自己学习。我们学了一种语言C#,没人会再教我们第二种语言,那么这第二种、第三种语言...需要我们自己来学了。比如这次逆波兰式我不会了,我就上网查啊查,折腾着,也想过要放弃不做了,但是我是那种看到别人能做出来相信自己也能做出来的人。所以我先是编写了一个控制台的小程序,一点一点测试,慢慢再加入到窗体,直到最后测试通过。才发现原来我是可以的!我觉得这种自主学习的方式我一定要坚持下去!通过这次结对编程,我认为:自主学习比较重要!!!