你的位置:首页 > Java教程

[Java教程]现代软件工程_第一周练习_第1题_万世想


第一题是要求实现一个自动生成小学生加减乘除四则运算题目的程序。后面可以将它扩展为网站或安卓应用或IOS应用或win10应用。

 

我的思路比较简单。环境是Java JDK1.8;IDE为Intellij社区版。

首先,我们不考虑有括号的情形,那么符号只有+、-、*、/四种;涉及到的数包含两种:整数或分数。

1. 我们注意到,一个运算式中,总有“数的个数比运算符多1”的规则。于是,我们自然地想到创建一个固定长度为n的数组number存放数字,每个数都能随机生成,通过maxOfNumber变量控制式子中出现的最大数字。创建一个长度为n-1的数组character存放运算符号。

2. 接下来需要初始化数组number和character。number数组每个元素靠maxOfNumber控制最大数、minOfNumber控制最小数、hasFraction控制是否出现分数随机生成;character数组每个元素靠type变量控制只有加减、只有乘除、加减乘除都有的3种类型随机生成。

例如:n为5,minOfNumber为0,maxOfNumber为10,hasFraction为出现分数,type为只有加减的类型:5/7+8/7-1/7-2/7

3. 接下来就是结果的计算。对于纯整数,计算结果是简单的。通过查找资料,我发现纯整数字符串的计算依靠ScriptEngine类即可完成。函数使用示例如下:

 1   //整型表达式的正确计算结果 2   public String calIntResult() { 3     ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript"); 4     try { 5       Double result = (Double) se.eval(formular); 6       return String.valueOf(result.doubleValue()); 7     } catch (ScriptException e) { 8       e.printStackTrace(); 9     }10     return null;11   }

 然而,对于分数,这种方法是不行的。比如1/5+2/3这样的式子,靠字符串渲染肯定行不通。依靠原始的数学求解方法,意识到需要借助求最大公约数和最小公倍数完成。例:求12和8的最小公倍数。12和8的最大公约数为4,12×8÷4=24 ,所以两数的最小公倍数是24。下面放上分数加减乘除法的简单实现。

 1 public class Fraction {   2   static int numera = 0; 3   static int deomina = 0; 4   5   public static void main(String[] args) {   6     int a1 = 9; 7     int a2 = 10; 8     int b1 = 3; 9     int b2 = 11;10     new Fraction().fracAdd(a1,a2,b1,b2);//结果为:11/20   11     System.out.println("分数加法运算:"+numera+"/"+deomina);12     new Fraction().fracSub(a1,a2,b1,b2);//分数相减   13     System.out.println("分数减法运算:"+numera+"/"+deomina);14     new Fraction().fracMul(a1,a2,b1,b2);//分数相乘   15     System.out.println("分数乘法运算:"+numera+"/"+deomina);16     new Fraction().fractDiv(a1,a2,b1,b2);//分数相除   17     System.out.println("分数除法运算:"+numera+"/"+deomina);18   }19   20   //定义分数相加函数21   public void fracAdd(int first_numerator, int first_denominator, int second_numrator, int second_denominator){  22     int lcm, gcd;23     lcm = lcm(first_denominator,second_denominator);//需要调用求最小公倍数的函数求的最小公倍数24     numera = (lcm/first_denominator)*first_numerator+(lcm/second_denominator)* second_numrator;// 未化简的分子和25     deomina = lcm;       //未化简的分母26     gcd = gcd(numera,deomina); //需要调用求最大公约数的函数27     numera = numera/gcd;    //化简后的分子28     deomina = deomina/gcd;   //化简后的分母29   }30 31   //定义分数相减函数32   public void fracSub(int first_numerator,int first_denominator,int second_numrator,int second_denominator){ 33     int lcm, gcd;34     lcm = lcm(first_denominator,second_denominator);//需要调用求最小公倍数的函数求的最小公倍数   35     numera = (lcm/first_denominator)*first_numerator-(lcm/second_denominator)* second_numrator;// 未化简的分子差36     deomina = lcm;       //未化简的分母37     gcd = gcd(numera,deomina); //需要调用求最大公约数的函数38     numera = numera/gcd;    //化简后的分子   39     deomina = deomina/gcd;   //化简后的分母   40   }41   42   //定义分数相乘函数43   public void fracMul(int first_numerator,int first_denominator,int second_numrator,int second_denominator){ 44     int z, m, gcd;45     z = first_numerator * second_numrator; 46     m = first_denominator * second_denominator; 47     gcd = gcd(z,m); 48     numera = z / gcd; 49     deomina = m / gcd; 50   }51   52   //定义分数相除函数53   public void fractDiv(int first_numerator,int first_denominator,int second_numrator,int second_denominator){ 54     int z, a, m, gcd;55     a = second_denominator; 56     second_denominator = second_numrator; 57     second_numrator = a; 58     z = first_numerator * second_numrator; 59     m = first_denominator * second_denominator; 60     gcd = gcd(z,m); 61     numera = z / gcd; 62     deomina = m / gcd; 63   }64   65   //求最大公约数66   public static int gcd(int m,int n){  67     int i = 2;    //定义整型变量i,为循环变量68     int g, min;  69     min = m>n ? n : m;70     g = 1;      //最大公约数初始值为171     while(i <= min) //判断条件,一直循环到两个数中较小的那个结束72     {  73       while (m % i == 0 && n % i == 0)  74       {  75         m = m / i;76         n = n /i;77         g = g * i;78       }  79       i++;  80     }  81     return g;  82   }83   84   //求最小公倍数函数85   public static int lcm(int m,int n){  86     int g, l;  87     g = gcd(m,n);    //调用求最大公约数函数88     l = m * n / g;  89     return l;  90   }   91 }  

4. 整数和分数的自动生成已经OK,计算结果方法也实现了。下面放上另一个类的具体代码。该代码与上面的代码一起实现了题目一的要求。

 1 public class AutoFormula { 2   static String formular = new String(); 3   static String result = new String(); 4   int numerator; 5   int denominator; 6    7   public static void main(String[] args) { 8     /* 9     * 参数mode为符号模式,0是加减、1是乘除、2是加减乘除; 10     * 参数hasFraction为控制分数个数; 11     * 参数numOfCharacter为符号数量; 12     * 参数minOfNumber为式子中出现的最小数值; 13     * 参数maxOfNumber为式子中出现的最大数值; 14     */ 15     new AutoFormula().generate(0, 1, 3, 0, 10); 16     System.out.println(formular); 17     System.out.println(result); 18   } 19   20   21   public void generate(int mode, int hasFraction, int numOfCharacter, int minOfNumber, int maxOfNumber) { 22     int numOfNumber = numOfCharacter + 1; 23     String[] character = new String[numOfCharacter]; 24     String[] number = new String[numOfNumber]; 25     StringBuilder stringBuilder = new StringBuilder(); 26     27     //初始化符号数组 28     for (int i=0; i<character.length; i++) { 29       character[i] = generateCharacter(mode); 30     } 31     //初始化数字数组 32     for (int i=0; i<number.length; i++) { 33       number[i] = generateNumber(hasFraction, minOfNumber, maxOfNumber); 34     } 35     //链接符号和数字 36     stringBuilder.append(number[0]); 37     for (int i=0; i<character.length; i++) { 38       stringBuilder.append(character[i] + number[i+1]); 39     } 40     formular = stringBuilder.toString(); 41     42     if (hasFraction == 0) { 43       result = calIntResult(); 44     } else { 45       result = calFractionResult(number, character); 46     } 47   } 48   49   50   //3种随机生成模式,随机生成加减、乘除、加减乘除符号 51   public String generateCharacter(int mode) { 52     Random random = new java.util.Random(); 53     int i; 54     switch (mode) { 55     case 0: 56       //+ - 57       i = random.nextInt(2); 58       return (i==0) ? "+" : "-"; 59     case 1: 60       //* / 61       i = random.nextInt(2); 62       return (i==0) ? "*" : "/"; 63     case 2: 64       //+ - * / 65       i = random.nextInt(4); 66       return (i==0) ? "+" : (i==1) ? "-" : (i==2) ? "*" : "/"; 67     } 68     return null; 69   } 70   71   72   //2种随机生成模式,随机生成整数、分数 73   public String generateNumber(int mode, int min, int max) { 74     Random random = new java.util.Random(); 75     switch (mode) { 76     case 0: 77       //随机生成整数 78       int num = min + random.nextInt(max); 79       while (num == 0) { //整数为0太简单 80         num = min + random.nextInt(max); 81       } 82       return String.valueOf(num); 83     case 1: 84       //随机生成分数 85       int numerator; 86       int denominator; 87       do { //分子为0太简单,分母不能为0或1,分子分母不能相等 88         numerator = min + random.nextInt(max); 89         denominator = min + random.nextInt(max); 90       } while (numerator == 0 || denominator == 0 || denominator == 1 || denominator == numerator); 91       return String.valueOf(numerator) + "/" + String.valueOf(denominator); 92     } 93     return null; 94   } 95 96   97   //整型表达式的正确计算结果 98   public String calIntResult() { 99     ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript");100     try {101       Double result = (Double) se.eval(formular);102       return String.valueOf(result.doubleValue());103     } catch (ScriptException e) {104       e.printStackTrace();105     }106     return null;107   }108   109   110   //分数型表达式的正确计算结果111   public String calFractionResult(String[] number, String[] character) {112     int first_numerator;113     int first_denominator;114     int second_numrator;115     int second_denominator;116     splitFraction(number[0]);117     first_numerator = numerator;118     first_denominator = denominator;119     for (int i=0; i<character.length; i++) {120       splitFraction(number[i+1]);121       second_numrator = numerator;122       second_denominator = denominator;123       Fraction fraction = new Fraction();124       switch (character[i]) {125       case "+":126         fraction.fracAdd(first_numerator, first_denominator, second_numrator, second_denominator);127         first_numerator = Fraction.numera;128         first_denominator = Fraction.deomina;129         break;130       case "-":131         new Fraction().fracSub(first_numerator, first_denominator, second_numrator, second_denominator);132         first_numerator = Fraction.numera;133         first_denominator = Fraction.deomina;134         break;135       case "*":136         new Fraction().fracMul(first_numerator, first_denominator, second_numrator, second_denominator);137         first_numerator = Fraction.numera;138         first_denominator = Fraction.deomina;139         break;140       case "/":141         new Fraction().fractDiv(first_numerator, first_denominator, second_numrator, second_denominator);142         first_numerator = Fraction.numera;143         first_denominator = Fraction.deomina;144         break;145       }146     }147     148     if (first_numerator == 0) {149       return "0";150     } else if (first_numerator == first_denominator) {151       return "1";152     } else {153       return first_numerator + "/" + first_denominator;154     }155   }156   157   158   //将分数拆分开159   public void splitFraction(String fraction) {160     if (fraction.contains("/")) {161       String[] tmpNumber = fraction.split("/");162       numerator = Integer.parseInt(tmpNumber[0]);163       denominator = Integer.parseInt(tmpNumber[1]);164     } else { //整数就直接分子分母相同165       numerator = Integer.parseInt(fraction);166       denominator = numerator;167     }168   }169 170 }

5. 做到这里,您可能会问:

为什么你要用Java语言实现呢?使用Python语言实现会不会更简单呢?

答:嗯,Python实现的确更简单,但是我们最后要做的当然是将它实现成一个网站或App。如果实现成网站,基于Spring MVC+Spring+Hibernate/Mybatis的Java Web拥有着更强大的逻辑表达能力以及更耦合、更稳定的技术架构。Java的强面向对象语言特性也能够更好地帮助团队严格化代码的编写,利于软件工程团队协作开发,Python弱语法的特点不利于规范代码,在后期维护更强大的功能时势必会复杂。PHP则只能应用于网站建设,后期不利于转型为Android应用,因此它不在我们的考虑范围内。.NET则只能用于微软服务器,这对于挚爱Linux的我。。。无法忍受。

为什么你要用Intellij IDE呢?Eclipse或者MyEclipse你咋不用?

答:这个问题已经讨论透了。我喜欢Intellij的原因是它非常完善的代码提示功能,即使你在写CSS或Javascript脚本时,它都有HTML5特性的语法提示,更别提Java各种类、包、函数的精巧提示了;此外,它无条件支持Maven,Gradle这些Java的项目管理工具,必须点个大写的赞。

6. 既然我们打算将这个服务做成网站,并且是基于Java SSH框架的,那么前端我暂时使用Bootstrap框架,上手简单,对于这个项目,足够了。

我们团队的Github代码托管地址为:https://github.com/NorthWolives/。里面包含基础的运算式生成代码以及目前网站前端实现的代码。

这是我的前端页面简单实现:http://server.malab.cn/PupilLearn/

首页截图如下,希望未来小朋友们喜欢:

 

时间:2016年9月9日

作者:万世想,天津大学计算机学院计算机科学与技术系