• 【个人项目总结】四则运算生成器


    项目需求:

    像阿超那样,花二十分钟写一个能自动生成小学四则运算题目的命令行 “软件”, 分别满足下面的各种需求。下面这些需求都可以用命令行参数的形式来指定:

        a) 除了整数以外,还要支持真分数的四则运算。 (例如:  1/6 + 1/8 = 7/24)

        b) 让程序能接受用户输入答案,并判定对错。 最后给出总共 对/错 的数量。

        c) 逐步扩展功能和可以支持的表达式类型,最后希望能支持下面类型的题目 (最多 10 个运算符,括号的数量不限制):
             25 - 3 * 4 - 2 / 2 + 89 = ?
             1/2 + 1/3 - 1/4 = ? 
            (5 - 4 ) * (3 +28) =?

        d) 一次可以批量出 100 道以上的题目,保存在文本文件中, 并且保证题目不能重复,(1+2) 和 (2+1) 是重复的题目。 

    我的实现:

    我了解到我的同学们大多都是用前、中、后缀表达式的互相转换来实现算式计算的,这个算法在上学期的课中编写过了,所以我想用个不一样的方法实现。

    我使用了C#的反射,动态生成、编译代码。这样,只要在程序中生成了中缀表达式,将其转换为String型然后插入动态代码中编译,即可直接算出结果。

    代码动态创建及编译的核心代码:

     1                 // 1.CSharpCodePrivoder
     2                 CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();
     3 
     4                 // 2.ICodeComplier
     5                 ICodeCompiler objICodeCompiler = objCSharpCodePrivoder.CreateCompiler();
     6 
     7                 // 3.CompilerParameters
     8                 CompilerParameters objCompilerParameters = new CompilerParameters();
     9                 objCompilerParameters.ReferencedAssemblies.Add("System.dll");
    10                 objCompilerParameters.GenerateExecutable = false;
    11                 objCompilerParameters.GenerateInMemory = true;
    12 
    13                 // 4.CompilerResults
    14                 GenerateLines();
    15                 Console.WriteLine("==========No." + (i+1).ToString() + "==========");
    16                 Console.WriteLine(lines);
    17 
    18                 CompilerResults cr = objICodeCompiler.CompileAssemblyFromSource(objCompilerParameters, GenerateCode());
    19 
    20                 if (cr.Errors.HasErrors)
    21                 {
    22                     Console.WriteLine("编译错误:");
    23                     foreach (CompilerError err in cr.Errors)
    24                     {
    25                         Console.WriteLine(err.ErrorText);
    26                     }
    27                 }
    28                 else
    29                 {
    30                     //通过反射,调用HelloWorld的实例
    31                     Assembly objAssembly = cr.CompiledAssembly;
    32                     object objHelloWorld = objAssembly.CreateInstance("DynamicCodeGenerate.HelloWorld");
    33                     MethodInfo objMI = objHelloWorld.GetType().GetMethod("OutPut");
    34                     string re = objMI.Invoke(objHelloWorld, null).ToString();
    35                 }

    其中,GenerateCode()是我用来生成代码的函数,返回了一个需要被动态编译的代码,其中定义了namespace DynamicCodeGenerate,public class HelloWorld以及其下方法public string OutPut()。

    代码生成函数GenerateCode()的主要功能即是生成算式。因为题目要求程序可以计算分数,所以GenerateCode()还需要能进行通分、约分的操作。

    GenerateCode()函数的主要流程:生成随机数,产生一个中缀表达式;对表达式全通分(即每一项都乘上所有被除项之积),然后将得数与刚才乘上的数约分,即得到分数形式的答案。

    之后接受用户输入,和标准答案比对后输出结果即可。

    完整代码:

      1 using System;
      2 using System.Reflection;
      3 using System.Globalization;
      4 using Microsoft.CSharp;
      5 using System.CodeDom;
      6 using System.CodeDom.Compiler;
      7 using System.Text;
      8 using System.Collections.Generic; 
      9 using System.IO;
     10 
     11 namespace A_Boring_Project_CSharp
     12 {
     13     class Program
     14     {
     15         static int max = 50;
     16         static int rows = 10;
     17         static List<int> nums;
     18         static List<int> add_n_minus;
     19         static List<int> div;
     20         static List<int> div_nums;
     21         static int tf;
     22         static int amount;
     23         static string lines;
     24         static string lines_tf;
     25         static string lines_final;
     26         static int count;
     27 
     28         static void Main(string[] args)
     29         {
     30             
     31             lines = "";
     32             count = 0;
     33             
     34             for (int i = 0; i < rows; i++)
     35             {
     36                 // 1.CSharpCodePrivoder
     37                 CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();
     38 
     39                 // 2.ICodeComplier
     40                 ICodeCompiler objICodeCompiler = objCSharpCodePrivoder.CreateCompiler();
     41 
     42                 // 3.CompilerParameters
     43                 CompilerParameters objCompilerParameters = new CompilerParameters();
     44                 objCompilerParameters.ReferencedAssemblies.Add("System.dll");
     45                 objCompilerParameters.GenerateExecutable = false;
     46                 objCompilerParameters.GenerateInMemory = true;
     47 
     48                 // 4.CompilerResults
     49                 GenerateLines();
     50                 Console.WriteLine("==========No." + (i+1).ToString() + "==========");
     51                 Console.WriteLine(lines);
     52 
     53                 CompilerResults cr = objICodeCompiler.CompileAssemblyFromSource(objCompilerParameters, GenerateCode());
     54 
     55                 if (cr.Errors.HasErrors)
     56                 {
     57                     Console.WriteLine("编译错误:");
     58                     foreach (CompilerError err in cr.Errors)
     59                     {
     60                         Console.WriteLine(err.ErrorText);
     61                     }
     62                 }
     63                 else
     64                 {
     65                     //通过反射,调用HelloWorld的实例
     66                     Assembly objAssembly = cr.CompiledAssembly;
     67                     object objHelloWorld = objAssembly.CreateInstance("DynamicCodeGenerate.HelloWorld");
     68                     MethodInfo objMI = objHelloWorld.GetType().GetMethod("OutPut");
     69                     string re = objMI.Invoke(objHelloWorld, null).ToString();
     70 
     71                     string answer = Console.ReadLine();
     72 
     73                     if (re == answer)
     74                     {
     75                         count++;
     76                         Console.WriteLine("Correct");
     77                     }
     78                     else
     79                     {
     80                         Console.WriteLine("Wrong, the answer should be " + re.ToString());
     81                     }
     82                 }             
     83             }
     84             Console.WriteLine("Your score is: " + count.ToString() + " out of " + rows.ToString());
     85             Console.ReadLine();
     86         }
     87 
     88         static string GenerateCode()
     89         {
     90             
     91             StringBuilder sb = new StringBuilder();
     92             sb.Append("using System;using System.IO;using System.Collections.Generic;using System.Text;");
     93             sb.Append(Environment.NewLine);
     94             sb.Append("namespace DynamicCodeGenerate");
     95             sb.Append(Environment.NewLine);
     96             sb.Append("{");
     97             sb.Append(Environment.NewLine);
     98             sb.Append("\tpublic class HelloWorld");
     99             sb.Append(Environment.NewLine);
    100             sb.Append("\t{");
    101             sb.Append(Environment.NewLine);
    102             sb.Append("\tpublic string OutPut()");
    103             sb.Append(Environment.NewLine);
    104             sb.Append("\t\t{");
    105             sb.Append(Environment.NewLine);
    106             sb.Append("long tf = "+ tf + ";");
    107             sb.Append(Environment.NewLine);
    108             sb.Append("long re = " + lines_final + ";");
    109             sb.Append(Environment.NewLine);
    110             sb.Append("for (int i = 2; i < re || i < tf; i++){while (re % i == 0 && tf % i == 0){re = re / i;tf = tf / i;}}");
    111             sb.Append(Environment.NewLine);
    112             sb.Append("Console.Write(\"Result = \");");
    113             sb.Append("\t\tif (tf != 1){return re.ToString() + \"/\" + tf.ToString();} else{return re.ToString();}");
    114             sb.Append(Environment.NewLine);
    115             sb.Append(Environment.NewLine);
    116             sb.Append("\t\t}");
    117             sb.Append(Environment.NewLine);
    118             sb.Append("\t}");
    119             sb.Append(Environment.NewLine);
    120             sb.Append("}");
    121 
    122             string code = sb.ToString();
    123 
    124             return code;
    125         }
    126 
    127         static void GenerateLines()
    128         {
    129             tf = 1;
    130             nums = new List<int>();
    131             add_n_minus = new List<int>();
    132             div = new List<int>();
    133             div_nums = new List<int>();
    134             System.Random rnd = new System.Random(GetRandomSeed());
    135             amount = rnd.Next(1, 11);
    136             lines = "";
    137             lines_tf = "";
    138             lines_final = "";
    139 
    140             int x = 0;
    141             int d = 0;
    142             List<int> anm;
    143             List<int> op;
    144             anm = new List<int>();
    145             op = new List<int>();
    146             for (int i = 0; i < amount; i++)
    147             {
    148                 //A new number
    149                 rnd = new System.Random(GetRandomSeed());
    150                 nums.Add(rnd.Next(1, max));
    151                 lines += nums[i];
    152                 //A new operator
    153                 System.Random rnd0 = new System.Random(GetRandomSeed());
    154                 int key = rnd0.Next(0,4);
    155                 op.Add(lines.Length + 1);
    156                 switch (key)
    157                 {
    158                     case 0:
    159                         anm.Add(i);
    160                         x = 0;
    161                         lines += "+";
    162                         add_n_minus.Add(lines.Length);
    163                         break;
    164                     case 1:
    165                         anm.Add(i);
    166                         x = 0;
    167                         lines += "-";
    168                         add_n_minus.Add(lines.Length);
    169                         break;
    170                     case 2:
    171                         x++;
    172                         if (x <= 3)
    173                         {
    174                             lines += "*";
    175                             break;
    176                         }
    177                         else
    178                         {
    179                             goto case 1;
    180                         }
    181                     case 3:
    182                         x = 0;
    183                         d++;
    184                         if (d <= 3)
    185                         {
    186                             lines += "/";
    187                             div.Add(i);
    188                             break;
    189                         }
    190                         else
    191                         {
    192                             goto case 1;
    193                         }
    194                     default:
    195                         x = 0;
    196                         break;
    197                 }
    198             }
    199             op.Add(lines.Length);
    200 
    201             rnd = new System.Random(GetRandomSeed());
    202             nums.Add(rnd.Next(1, max));
    203             lines += nums[amount];
    204             
    205             for (int i = 0; i < div.Count; i++)
    206             {
    207                 div_nums.Add(nums[div[i] + 1]);
    208                 lines_tf += div_nums[i];
    209                 lines_tf += "*";
    210                 tf *= div_nums[i];
    211             }
    212 
    213             lines_final += lines_tf;
    214             lines_final += lines;
    215 
    216             if (div.Count > 0)
    217             {
    218                 for (int i = 1; i <= add_n_minus.Count; i++)
    219                 {
    220                     lines_final = lines_final.Insert(i * lines_tf.Length + add_n_minus[i - 1], lines_tf);
    221                 }
    222             }
    223             
    224         }
    225 
    226         static int GetRandomSeed()
    227         {
    228             byte[] bytes = new byte[4];
    229             System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
    230             rng.GetBytes(bytes);
    231             return BitConverter.ToInt32(bytes, 0);
    232         }
    233         
    234     }
    235 }

    小结:

    在写GenerateCode()函数时,没有预先设计好控制、变量等等的具体实现,所以最终导致代码有点混乱,下次应该多加注意。

  • 相关阅读:
    Hadoop-03 基于Hadoop的JavaEE数据可视化简易案例(升级使用HBase存储结果集)
    Hadoop-02 基于Hadoop的JavaEE数据可视化简易案例
    Python03 变量
    Python01 VSCode开发环境和入门程序
    MyBatis01 Idea中搭建MyBatis开发环境
    Idea01 Idea2018中集成Tomcat9导致OutPut乱码
    MySQL-08 MySQL8.0新特性
    C11 C语言文件的读写
    C10 C语言数据结构
    C09 指针
  • 原文地址:https://www.cnblogs.com/yuki8819/p/5305107.html
Copyright © 2020-2023  润新知