一、基本想法

1、整数和分数的四则运算

        由于四则运算要支持分数,刚开始我想着是自定义分数这种数据类型,而后再重载运算符。可当整数和分数混合运算的话,就要考虑到数据类型间的转化,比较麻烦。于是我转化了下思路,即将整数看成是特殊的分数(分母为1),这样将整数和分数统一起来,那么初始化便可以根据具体的数据类型做出调整,如下图所示:

小学生四则运算应用软件(一)

    这里需要特别注意的是,我们最后控制台显示的算式是String类型,因而要能实现分数Fraction和String直接的相互转化。

小学生四则运算应用软件(一)

     而且要自定义一种隐式转化,这样就能令分数表示出正确的形式(比如整数10不显示分母,分数2/5等)

小学生四则运算应用软件(一)

     这时问题又出来了,分数2/4等同于1/2,即在实际应用中分数要进行相应化简,解决方案是求取分母分子的最大公约数,并且如果分母为负数,则将分子分母同时乘以-1(为了比较大小乘以分子大小符号不会改变),如下图所示:

小学生四则运算应用软件(一)

小学生四则运算应用软件(一)

     最后便是运算符的重载了,这里以+、<、==为例

小学生四则运算应用软件(一)

小学生四则运算应用软件(一)

小学生四则运算应用软件(一)

     其中Add函数具体实现如下:

小学生四则运算应用软件(一)

      由于减法等同于加一个负数,除法实际上是乘一个倒数,这里就不再赘述

      另外分子不能为0,因此要进行相应的异常处理。

小学生四则运算应用软件(一)

2、运算符的扩展

     实际应用中四则运算不单单局限于二元运算,而是可以有多个操作符的混合运算,显然正常的算式顺序是很难计算的,这时候就应该把算式转化成逆波兰式。具体操作步骤如下:

(1)首先把普通的表达式按照运算符分离出来放在一个集合S中,比如3+2*5 分离后集合里的元素就是 3 + 2 * 5 五个元素

(2)再定义一个集合T(为了省去转化类型的麻烦,一般为String),主要用来存放逆波兰表达式的,除此之外需定义一个堆栈K以便存储运算符,最后从左到右遍历集合S     

 (3)遍历E的规则如下:

   (3.1)如果该元素是数字(这里是Fraction转化的String),直接把它添加到集合T中

   (3.2)否则它肯定是运算符,那么再进行判断

        (3.2.1)如果该元素是左括号,或者当时栈为空,那么直接入栈

        (3.2.2)如果该元素是右括号,则把栈内的运算符出栈并添加到集合T中,直到遇到第一个左括号结束(左括号也出栈但不添加到T)

        (3.2.3)否则该元素是普通的运算符(也就是+-*/之类的),那么用该运算符和栈内的运算符号比较优先级,如果该运算符的优先级比栈内的运算符优先级高或者栈为空,则直接入栈,否则把栈内的运算符出栈并添加到T中,再判断下个栈内的运算符优先级,直到栈内的运算符优先级<=该运算符或者栈为空时再                    把该运算符入栈

   这里运算符优先级的定义使用的是Dictionary的数据结构,如图所示:

小学生四则运算应用软件(一)

小学生四则运算应用软件(一)

     具体函数实现为 static Queue<object> PreOrderToPostOrder(List<string> expression),最后返回的是集合T

计算逆波兰式的规则相应比较简单,即

(1)从左到右遍历T

(2)如果该元素是数字,直接入栈

(3)如果该元素是运算符,出栈两个数,计算结果再入栈,逆波兰遍历完后栈内的元素就是表达式的值了。函数实现如下:

小学生四则运算应用软件(一)

3、程序流程

    目前程序设计流程为:

(1)询问用户生成四则运算的题数

(2)询问用户是否自己输入答案,若输入,完成后统计用户答题的正确数

(3)询问用户是否显示正确答案

(4)询问用户是否继续生成四则运算的题数,若否则询问是否生成题目文件,若要生成题目文件,则再询问生成题目的总题数

     从流程中可以看到程序的重中之重是要生成算式,起初我的想法是随机生成运算符(括号除外)的个数,那么表达式其实就是操作数+运算符+操作数的形式,那么相应操作数的个数其实就比运算符的个数多1,可一旦考虑到括号,数组的结构无法满足在原先算式的基础上添加括号,因而我使用的是List这一数据结构,方便插入修改。另外为了表示是操作数还是运算符,除去定义str这一List,还定义了isOperand列表(isOperand的元素为0表示为运算符,为正整数表示第几个操作数,如等于1便是第一个操作数)

小学生四则运算应用软件(一)

     这样的话随机了括号的个数numOfBrackets,并随机括号的初始和结束位置(表示在第几个操作数前或后,即start和end),而后再将(、)插入到List str里,相应的isOperand插入-1,表示括号

小学生四则运算应用软件(一)

        最后我们在List str首部插入通过已插入元素组合成的算式兵返回。举例2*(5+2)来说,那么str[0] = 2*(5+2),str[1]=2,str[2]=*,str[3]=5,str[4]=+,

str[5]=2,str[6]=)。(这里space表示“ ”,主要是为了算式美观,将分数的/和除法的/区分开,另外考虑到以后可能要加负数,防止混淆)

小学生四则运算应用软件(一)

二、代码实现

    代码使用C#语言实现,主要包含Program.cs和Fraction.cs两个文件,前者是主程序,后者是分数类的具体实现。

(1)Program.cs

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 using System.IO;
  7 using Fra;
  8 
  9 
 10 namespace CalC
 11 {
 12     class Program
 13     {
 14         static Random ran = new Random();
 15         static Dictionary<string, int> priorities = null;
 16         const string operators = "+-*/=";
 17         const string space = " ";
 18         static Program()
 19         {
 20             priorities = new Dictionary<string, int>();
 21             priorities.Add("#", -1);
 22             priorities.Add("+", 0);
 23             priorities.Add("-", 0);
 24             priorities.Add("*", 1);
 25             priorities.Add("/", 1);
 26         }
 27 
 28         static void Main(string[] args)
 29         {
 30             int input = 1;
 31             while (input == 1)
 32             {
 33                 Console.Write("请输入生成四则运算题的数目: ");
 34                 int numOfQue = int.Parse(Console.ReadLine());
 35                 string[] inputAnswer = new string[numOfQue];
 36                 string[] correctAnswer = new string[numOfQue];
 37                 string[] ques = new string[numOfQue];
 38                 List<string> result = new List<string>();
 39                 for (int i = 0; i < numOfQue; i++)
 40                 {
 41                     result = produceQue();
 42                     ques[i] = result[0];
 43                     Console.Write("{0,-20}", ques[i] + operators[4]);
 44                     correctAnswer[i] = Calucate(result);
 45                 }
 46                 Console.WriteLine();
 47                 Console.Write("是否输入答案(输入1表示用户输入答案,否则不输入): ");
 48                 input = int.Parse(Console.ReadLine());
 49                 if (input == 1)
 50                 {
 51                     for (int i = 0; i < numOfQue; i++)
 52                     {
 53                         Console.Write("{0,-20}", ques[i] + operators[4] + space);
 54                         inputAnswer[i] = Console.ReadLine();
 55                     }
 56 
 57                     int numOfCorrect = 0;
 58                     for (int i = 0; i < numOfQue; i++)
 59                     {
 60                         if (inputAnswer[i] == correctAnswer[i])
 61                             numOfCorrect++;
 62                     }
 63                     Console.WriteLine("您共答对" + numOfCorrect + "道题");
 64                 }
 65 
 66 
 67                 Console.Write("是否显示正确答案(输入1表示显示正确答案,否则不显示): ");
 68                 input = int.Parse(Console.ReadLine());
 69                 if (input == 1)
 70                 {
 71                     for (int i = 0; i < numOfQue; i++)
 72                         Console.Write("{0,-20}", ques[i] + operators[4] + space + correctAnswer[i]);
 73                     Console.WriteLine();
 74                 }
 75                 Console.Write("是否继续生成四则运算题的数目(输入1继续生成,否则不生成): ");
 76                 input = int.Parse(Console.ReadLine());
 77                 Console.Clear();
 78             }
 79 
 80             Console.Write("是否生成题目文件(输入1生成,否则不生成): ");
 81             input = int.Parse(Console.ReadLine());
 82             if (input == 1)
 83             {
 84                 Console.Write("输入生成题目的数量: ");
 85                 string filename = "que.txt";//这里是你的已知文件
 86                 FileStream fs = File.Create(filename);  //创建文件
 87                 fs.Close();
 88                 StreamWriter sw = new StreamWriter(filename);
 89                 input = int.Parse(Console.ReadLine());
 90                 for (int i = 0; i < input; i++)
 91                 {
 92                     string que = "";
 93                     que = produceQue()[0];
 94                     sw.Write("{0,-20}",que + operators[4] + space);
 95                     if (i % 10 == 9)
 96                         sw.Write("\r\n");
 97                 }
 98                 sw.Close();
 99             }
100         }
101 
102 
103         static List<string> produceQue()
104         {
105             List<string> str = new List<string>();
106             List<int> isOperand = new List<int>();
107             int count = 0;
108             count = ran.Next(1, 3);
109             int[] num = new int[count + 1];
110             int[] den = new int[count + 1];
111             string[] operand = new string[count + 1];
112             int[] index = new int[count];
113             int numOfBrackets = 0;
114             for (int i = 0; i < count + 1; i++)
115             {
116                 num[i] = ran.Next(2, 5);
117                 if (ran.Next(1, 10) < 8)
118                     den[i] = 1;
119                 else
120                 {
121                     den[i] = ran.Next(1, 5);
122                     numOfBrackets = ran.Next(1, count);
123                 }
124                 operand[i] = new Fraction(num[i], den[i]).ToString();
125                 if (i < count)
126                     index[i] = ran.Next(0, 4);
127             }
128             int[] start = new int[numOfBrackets];
129             int[] end = new int[numOfBrackets];
130             for (int i = 0; i < numOfBrackets; i++)
131             {
132                 start[i] = ran.Next(1, count + 1);
133                 end[i] = ran.Next(start[i] + 1, count + 2);
134             }
135             int j = 1;
136             for (int i = 0; i < count + 1; i++)
137             {
138                 str.Add(operand[i]);
139                 isOperand.Add(i + 1);
140                 if (i < count)
141                 {
142                     str.Add(operators[index[i]].ToString());
143                     isOperand.Add(0);
144                 }
145             }
146             for (int i = 0; i < numOfBrackets; i++)
147             {
148                 int left = isOperand.FindIndex(s=>s==start[i]);
149                 str.Insert(left, "(");
150                 isOperand.Insert(left, -1);
151                 int right = isOperand.FindIndex(s =>s==end[i]);
152                 str.Insert(right + 1, ")");
153                 isOperand.Insert(right + 1, -1);
154             }
155             str.Insert(0, "");
156             for (int i = 1; i < str.Count;)
157             {
158                 str[0] += str[i++] + space;
159             }
160             return str;
161         }
162 
163 
164 
165 
166         static string Compute(Fraction leftNum, Fraction rightNum, int op)
167         {
168             switch (op)
169             {
170                 case 0: return leftNum + rightNum;
171                 case 1: return leftNum - rightNum;
172                 case 2: return leftNum * rightNum;
173                 case 3: return leftNum / rightNum;
174                 default: return "";
175             }
176         }
177 
178         static bool IsOperator(string op)
179         {
180 
181             return operators.IndexOf(op) >= 0;
182         }
183 
184         static bool IsLeftAssoc(string op)
185         {
186             return op == "+" || op == "-" || op == "*" || op == "/" || op == "%";
187         }
188 
189         static Queue<object> PreOrderToPostOrder(List<string> expression)
190         {
191             var result = new Queue<object>();
192             var operatorStack = new Stack<string>();
193             operatorStack.Push("#");
194             string top, cur, tempChar;
195             string tempNum;
196 
197             for (int i = 1; i < expression.Count; )
198             {
199                 cur = expression[i++];
200                 top = operatorStack.Peek();
201 
202                 if (cur == "(")
203                 {
204                     operatorStack.Push(cur);
205                 }
206                 else
207                 {
208                     if (IsOperator(cur))
209                     {
210                         while (IsOperator(top) && ((IsLeftAssoc(cur) && priorities[cur] <= priorities[top])) || (!IsLeftAssoc(cur) && priorities[cur] < priorities[top]))
211                         {
212                             result.Enqueue(operatorStack.Pop());
213                             top = operatorStack.Peek();
214                         }
215                         operatorStack.Push(cur);
216                     }
217                     else if (cur == ")")
218                     {
219                         while (operatorStack.Count > 0 && (tempChar = operatorStack.Pop()) != "(")
220                         {
221                             result.Enqueue(tempChar);
222                         }
223                     }
224                     else
225                     {
226                         tempNum = cur;
227                         result.Enqueue(tempNum);
228                     }
229                 }
230             }
231             while (operatorStack.Count > 0)
232             {
233                 cur = operatorStack.Pop();
234                 if (cur == "#") continue;
235                 if (operatorStack.Count > 0)
236                 {
237                     top = operatorStack.Peek();
238                 }
239 
240                 result.Enqueue(cur);
241             }
242 
243             return result;
244         }
245 
246         static string Calucate(List<string> expression)
247         {
248 
249             var rpn = PreOrderToPostOrder(expression);
250             var operandStack = new Stack<string>();
251             string left, right;
252             object cur;
253             while (rpn.Count > 0)
254             {
255                 cur = rpn.Dequeue();
256                 int index = operators.IndexOf(cur.ToString());
257 
258                 if (index >= 0)
259                 {
260                     right = operandStack.Pop();
261                     left = operandStack.Pop();
262                     operandStack.Push(Compute(left, right, index));
263                 }
264                 else
265                 {
266                     operandStack.Push(cur.ToString());
267                 }
268             }
269             return operandStack.Pop();
270         }
271     }
272 }
View Code

相关文章:

  • 2022-12-23
猜你喜欢
  • 2021-05-30
  • 2021-12-04
  • 2021-07-13
  • 2021-05-19
相关资源
相似解决方案