标签:scan eval string 异常 算法 技术 bool method express
程序设计思想:
大体的思路还是不变,用生成随机数的方式生成若干个操作数,并且设置不同的参数来控制算术题的格式。但是这次的程序有了新的需求,而且新的需求实现过程更加困难,若是还是按照结构化思想来解决问题,将面临巨大的困难。所以,此次我用面向对象的方法来解决问题。
将算术式封装成一个类,用字符串来存放一个算式。在这个类中,主要的问题还是如何构造算式。运算符、操作数和操作数的个数都可以用随机数来生成。但是,有三项难题需要解决:控制乘除有没有余数、加减有没有复数和控制有没有括号。要控制结果首先需要求出结果。现在已经不再是2个数一种运算符的求值了。多个数、多个优先级的运算符还有括号需要考虑,这是不可能在生成运算符和操作数时就可以得到正确结果的。所以我用到了数据结构中学到的一种算法——应用栈将中缀表达式化为后缀表达式,然后计算表达式结果。这个算法是去年学的,算法的步骤我还记忆犹新,但是实现的细节已经不太熟了,怎出这个算法也是花了我不少时间。第二个问题就是在支持余数的情况下怎么运算。整型的数据相除必然还会是整型数据,这样肯定得不到正确答案,所以为了解决这个问题,我还设计了另一个类Fraction类用来进行分数的运算符。将整数都化为分数,最后得到的结果就都是准确结果了。第三个问题,怎么让式子中包含括号。这个问题倒是真的难倒我了。想来想去我也只有一个办法,大致想法是先在第N个操作数前插入前括号,然后在第N+2个操作数后插入后括号,N为大于0小于操作数个数的一个随机数。这个方法基本上解决了问题,但是这只能为一个算式插入一对括号。
程序源代码:
因为怎个程序代码较长,所以以下仅展示核心类的实现。
1 public class Equation { 2 3 public static void main(String[] args) { 4 // TODO Auto-generated method stub 5 boolean flag = true; 6 boolean sc = flag,fs=flag,kh=flag,fus=flag; 7 int mun = 0,beg=0,end=100; 8 9 Scanner scan = new Scanner(System.in); 10 while(flag){ 11 try{ 12 System.out.println("请输入表达式的个数:"); 13 mun = scan.nextInt(); 14 System.out.println("是否支持乘除法?(true/false)"); 15 sc = scan.nextBoolean(); 16 if(sc){ 17 System.out.println("除法是否有余数?(true/false)"); 18 fs = scan.nextBoolean(); 19 } 20 System.out.println("是否有括号?(true/false)"); 21 kh = scan.nextBoolean(); 22 System.out.println("是否有负数?(true/false)"); 23 fus = scan.nextBoolean(); 24 flag = false; 25 }catch(Exception e){ 26 System.out.println("输入错误!请重新输入:"); 27 flag = true; 28 } 29 } 30 Equation array[] = new Equation[mun]; 31 for(int i = 0; i < mun; i++){ 32 array[i] =new Equation(beg,end,(int)(Math.random()*8)+2,sc,fs,kh,fus); 33 System.out.println((i+1)+":"+array[i].getExpress()+"="+array[i].getValue()); 34 } 35 scan.close(); 36 } 37 public static final boolean 38 SUPPORT_FRACTION=true, //支持分数 39 NONSUPPORT_FRACTION=false, //不支持分数 40 NONSUPPORT_MINUS=false, //不支持支持复数 41 SUPPORT_MINUS=true, //支持复数 42 NONSUPPORT_MULTIPLICATION=false,//增加常量表示没有乘除法 43 SUPPORT_MULTIPLICATION=true, //表示含有乘除法 44 NONSUPPORT_BRACKET=false, //表示没有括号 45 SUPPORT_BRACKET=true; //表示带括号 46 47 48 private String express,value; 49 50 /** 51 * 2017年3月10日修改记录: 52 * 将原来用静态方法生成表达式的方法该为用构造方法生成表达式,更加符合面向对象的程序设计思路。 53 * */ 54 public Equation() { 55 // TODO Auto-generated constructor stub 56 this(0,100,10,true,true,true,true); 57 } 58 public Equation(int beg,int end,int n){ 59 this(beg,end,n,true,true,true,true); 60 } 61 public Equation(boolean mul,boolean fraction,boolean bra,boolean minus){ 62 this(0,100,10,mul,fraction,bra,minus); 63 } 64 public Equation(String e){ 65 express = e; 66 try { 67 evaluation(true,true); 68 } catch (Exception e1) { 69 // TODO Auto-generated catch block 70 e1.printStackTrace(); 71 } 72 } 73 public Equation(int beg,int end,int m,boolean mul,boolean fraction,boolean bra,boolean minus) 74 //beg表示操作数的最小值,end表示最大值,n表示操作数的个数, 75 //mul表示是否支持乘除,bra是否支持括号,minus表示是否有负数,fraction表示是否支持分数 76 { 77 createExpress(beg,end,m,mul,fraction,bra,minus); 78 //计算结果 79 boolean tag = true; 80 while(tag){ 81 try {//如果计算结果时出现异常,将递归调用本方法为对象重新赋值 82 this.evaluation(fraction,minus); 83 tag = false; 84 } catch (Exception e) { 85 createExpress(beg,end,m,mul,fraction,bra,minus); 86 tag = true; 87 } 88 } 89 } 90 91 //setter and get 92 public String getExpress(){return express;} 93 94 public void setExpress(String ex){express = ex;} 95 96 public String getValue(){return value;} 97 98 public String toString(){ 99 return express+"="+value; 100 } 101 public boolean equals(Equation another)//比较两个算式是否相同 102 { 103 String str1 = new String(this.express); 104 String str2 = new String(another.express); 105 str1.replaceAll(" ", ""); 106 str2.replaceAll(" ", ""); 107 if(str1.equals(str2)) 108 return true; 109 110 return false; 111 } 112 113 private void createExpress(int beg,int end,int m,boolean mul,boolean fraction,boolean bra,boolean minus) 114 //生成一个表达式 115 { 116 //数组operand存放express中的所有操作数 117 int n = m; 118 int operand[] = new int[n]; 119 //operator存放所有操作符 120 char operator[] = new char[n]; 121 //oper存放可选择的操作符,如果mul为false,则没有乘除法 122 char[] oper = {‘+‘,‘-‘,‘*‘,‘/‘}; 123 if(!mul) {oper[2]=‘+‘;oper[3]=‘-‘;} 124 String ss=new String(""); 125 for(int i = 0; i < n; i++){ 126 operand[i] =(int) (Math.random()*(end-beg))+beg; 127 operator[i]=oper[operand[i]%4]; 128 ss =ss+ operand[i]+operator[i]; 129 } 130 131 if(bra&&((int)( Math.random()*100))>50){ 132 //在这里为express中插入一对括号 133 134 int first = (int)(Math.random()*(n-1))+1;//括起来的第一个数 135 int next = (int)(Math.random()*(n-1))+1;//括起来的最后一个数 136 if(first > next){//如果第一个数比第二个数大,则交换两个数的值 137 first = first^next; 138 next = first^next; 139 first=first^next; 140 } 141 if(next-first >= 2){//如果next和first的差值小于2,则不执行以下步骤,因为括号中至少有两个数 142 //length记下表达式字符串的长度,j记下遍历字符串中数字的个数 143 int length = ss.length(),j=0; 144 //用字符串建立一个动态字符串。 145 StringBuffer temp = new StringBuffer(ss); 146 //开始循环遍历字符串 147 for(int i=0; i < length; i++){ 148 char cc = temp.charAt(i);//取出下标为i的字符 149 if(first==1&&i==0) //如果括号括起来的第一个数字为表达式中第一个操作数,在表达式最前面插入( 150 temp.insert(0, ‘(‘); 151 if(cc > ‘9‘ || cc < ‘0‘){//如果当前字符是运算符字符 152 j++; //表示刚刚遍历一个数字,所以j加一 153 if(j==first-1){ //如果j到达first的前一个位置,在当前字符的后面插入“(” 154 temp.insert(i+1, ‘(‘);i++; 155 } 156 if(j==next) //如果j到达next的位置,在当前字符前面插入一个“)” 157 temp.insert(i, ‘)‘); 158 } 159 } 160 ss = temp.toString();//将动态字符串转换为普通字符串 161 } 162 } 163 express = ss.substring(0, ss.length()-1); 164 } 165 private void evaluation(boolean yushu,boolean fushu) throws Exception//表达式求值 166 { 167 //申请一个存放分数的栈和一个存放操作符的栈 168 java.util.Stack<Fraction> mStack = new java.util.Stack<Fraction>(); 169 java.util.Stack<Character> oStack= new java.util.Stack<Character>(); 170 171 String express = new String(this.express+"#"); 172 boolean flag = false;//旗帜变量标识上一个字符是否为数字字符 173 int i = 0,//字符串的下标变量 174 len = express.length();//字符串的长度 175 oStack.push(‘#‘); 176 while(i < len){ 177 char ch = express.charAt(i);//取出计算式中第i个字符 178 //如果当前字符为空字符,跳过这个字符 179 if(ch==‘ ‘||ch==‘\t‘||ch==‘\n‘)i++; 180 //如果是数字 181 else if(ch >= ‘0‘ && ch <= ‘9‘){ 182 if(flag){ 183 //如果前一个字符为数字字符,从mStack栈中弹出一个分数 184 //乘以10加上ch代表的数字后,再压入mStack栈中。 185 Fraction t1 = mStack.pop(); 186 t1 = t1.multiply(10); 187 t1 = t1.add(ch-‘0‘); 188 mStack.push(t1); 189 flag = true;//旗帜变量依然是true,标志当前字符为数字字符 190 }else{ 191 //如果前一个字符不是数字字符,生成一个新的分数压入mStack中 192 mStack.push(new Fraction(ch-‘0‘)); 193 flag = true;//标志当前字符为数字字符 194 }i++;//下一个字符 195 }else{ 196 switch(precede(oStack.peek(),ch)){ 197 //比较oStack栈顶操作符的优先级和操作符ch的优先级 198 case ‘<‘://栈顶操作符的优先级大于ch优先级 199 oStack.push(ch); 200 i++;break; 201 case ‘>‘: 202 //小于ch优先级,从分数栈中弹出两个分数按照运算符栈顶的操作运算,结果压入分数栈 203 Fraction b = mStack.pop(); 204 Fraction a = mStack.pop(); 205 Fraction c = calculate(a,oStack.pop(),b); 206 if(!yushu && c.getDenominator() > 1) 207 throw new Exception("计算过程出现余数!"); 208 if(!fushu && c.lessThan(new Fraction(0))) 209 throw new Exception("计算过程出现负数!"); 210 mStack.push(c);break; 211 case ‘=‘: 212 //优先级相等的情况 213 oStack.pop();//弹出运算符栈顶的运算符 214 i++; 215 break; 216 } 217 flag = false;//标志当前字符为运算符 218 } 219 } 220 value=mStack.peek().toString(); 221 222 } 223 private Fraction calculate//将分数pop和peek安照pop2代表的运算符计算 224 (Fraction pop, Character pop2, Fraction peek)throws Exception 225 { 226 // TODO Auto-generated method stub 227 if(pop2==‘+‘) 228 return pop.add(peek); 229 else if(pop2==‘-‘) 230 return pop.reduce(peek); 231 else if(pop2==‘*‘) 232 return pop.multiply(peek); 233 else if(pop2==‘/‘) 234 return pop.divide(peek); 235 return null; 236 } 237 private static char precede(char s,char t)//比较运算符s和t的优先级 238 { 239 char tag=‘<‘; //临时变量,用来保存比较结果 240 switch(s){ 241 case ‘+‘: 242 if(t == ‘*‘ || t == ‘/‘ || t == ‘(‘) 243 tag = ‘<‘; 244 else 245 tag = ‘>‘; 246 break; 247 case ‘-‘: 248 if(t == ‘*‘ || t == ‘/‘ || t == ‘(‘) 249 tag = ‘<‘; 250 else 251 tag = ‘>‘; 252 break; 253 case ‘*‘: 254 if(t == ‘(‘) 255 tag = ‘<‘; 256 else 257 tag = ‘>‘; 258 break; 259 case ‘/‘: 260 if(t == ‘(‘) 261 tag = ‘<‘; 262 else 263 tag = ‘>‘; 264 break; 265 case ‘(‘: 266 if(t == ‘)‘) 267 tag = ‘=‘; 268 else if(t == ‘#‘) 269 break; 270 else 271 tag = ‘<‘; 272 break; 273 case ‘)‘: 274 if(t == ‘(‘) 275 break; 276 else 277 tag = ‘>‘; 278 break; 279 case ‘#‘: 280 281 if(t == ‘)‘) 282 break; 283 else if(t == ‘#‘) 284 tag = ‘=‘; 285 else 286 tag = ‘<‘; 287 break; 288 } 289 290 return tag; 291 } 292 }
程序结果截图:
项目计划日志:
时间记录日志:
缺陷记录日志:
标签:scan eval string 异常 算法 技术 bool method express
原文地址:http://www.cnblogs.com/maosonglin/p/6532653.html