为了替换项目里面的bsh-core,自己写了个eval

自己的game需要代码尽量小,但是bsh-core就占了200多k,很不爽,想想自己的代码中,需要用到bsh的地方就是计算java中带 +-*/%()等的计算公式,干脆,自己来写一个吧

除了异常比较难控制,基本都能实现了,代码量一下从291k下降到了11k


抛砖引玉,代码写的不是很pattern,只是希望能找到一些同好的朋友,引起一些共鸣,需要上一个game里面的高级算法,请联系我


主程序分4步,分别有去括号和计算运算符,计算运算符还分优先级,请注意注释部分




/*******************************************************************************/
public static final String ERROR_POWER = "can't use 1 ^ X = 1 this exp!";
public static final String ERROR_START = "can't start with symb!";
public static final String ERROR_EMPTY_EXP = "empty exp!";
public static final String ERROR_EMPTY_VAL = "empty exp value!";
public static final String ERROR_EXPERT = "error expert epx!";
public static final String ERROR_LEFT_BRACKET = "no match \"( \"";
public static final String ERROR_MOD = "can't use X mod "
+ Constant.ERROR_MOD + " this exp!";
public static final String ERROR_DIV_ZERO = "can't divide by zero!";
public static final String ERROR_INFINITY = "Infinity!";
public static final String ERROR_UNKNOWN = "unknow expression error occured!";

// public static final String Utility65="no expert exp..*&^*#%(@";

private static class ExpressionException extends RuntimeException {
public ExpressionException(String message) {
super(message);
}
}

/**
* 用BSH计算表达式的运行结果,用于用户计算
*/
public static String calculateExpressionValue(String exp)
throws ExpressionException {
String temp;
// 1. format
temp = format(exp);
if (Constant.DEBUG)
System.out.println("format: from: " + exp + ", to: " + temp);
exp = temp;
// 2. get ride of "(" and ")".
while (exp.indexOf(") ") > 0) {
int end = exp.indexOf(") ");
int start = exp.substring(0, end).lastIndexOf("( ") + 2;
if (start < 2)
throw new ExpressionException(ERROR_LEFT_BRACKET);
String middleExp = exp.substring(start, end);
temp = calculateExpressionValue(middleExp);
// TODO bug: 8/(1/(11-8))
if (Constant.DEBUG)
System.out.println("trans: \"" + middleExp + "\" to \"" + temp
+ "\"");
exp = exp.substring(0, start - 2) + temp + " "
+ exp.substring(end + 2);
}

// 3. calc the ^,l,√,expertExp value
while (indexofExpert(exp) >= 0) {
// the expert symb start
int i = indexofExpert(exp);
if (Constant.DEBUG)
System.out.println("exp=" + exp + ",i=" + i);
if (exp.substring(i, i + 1).equals("l")) {
if (Constant.DEBUG)
System.out.println("1");
// if: log 2 8 -> 2 log 8
// ... + log 2 8
if ((i == 0 || !NumberUtils.isNumber(wordBefore(exp, i)))
&& NumberUtils.isNumber(wordAfter(exp, i, 2))) {
if (Constant.DEBUG)
System.out.println("2");
String a = wordAfter(exp, i);
a = exp.substring(0, i) + a + " " + "l" + " "
+ exp.substring(i + 3 + a.length());
if (Constant.DEBUG)
System.out.println("trans exp from: " + exp + ", to: "
+ a);
exp = a;
i = indexofExpert(exp);
}
}
if (exp.substring(i, i + 1).equals("^")) {
if (!Constant.eliminatePower
&& Float.parseFloat(wordBefore(exp, i)) == 1) {
throw new ExpressionException(ERROR_POWER);
}
}
if (exp.substring(i, i + 1).equals("%")) {
if (Float.parseFloat(wordAfter(exp, i)) < Constant.MIN_MOD) {
throw new ExpressionException(ERROR_MOD);
}
}
if (Constant.DEBUG)
System.out.println("exp=" + exp + ",i=" + i);
if (i < 2)
throw new ExpressionException(ERROR_START);
// get the num before symb.
int start = exp.substring(0, i - 1).lastIndexOf(" ") + 1;
// TODO symb length
int t = exp.substring(i + 1 + 1).indexOf(" ");
int end = i + 2 + (t < 0 ? exp.substring(i + 2).length() : t);
String expertStr = exp.substring(start, end);
// throw
temp = evalExpert(expertStr);
if (Constant.DEBUG)
System.out.println("expert: \"" + expertStr + "\" to \"" + temp
+ "\"");
exp = exp.substring(0, start) + temp + " "
+ (t < 0 ? "" : exp.substring(end + 1));
}
// 4. simple eval
String[] doubleExps = exp.split(" ");
for (int i = 0; i < doubleExps.length; i++) {
String e = doubleExps[i];
if (e.length() > 0 && NumberUtils.isDigits(e) && e.indexOf('.') < 0) {
doubleExps[i] += ".0";// trans to double
}
}
exp = "";
for (String e : doubleExps) {
exp += e + " ";
}
if (Constant.DEBUG)
System.out.println("double: trans to: " + exp);
return evalBase(exp);

}

private static String evalBase(String exp) throws ExpressionException {
int i = indexofMulDiv(exp);
while (i > 0) {
String sym = exp.substring(i, i + 1);
String num1 = wordBefore(exp, i);
String num2 = wordAfter(exp, i);

int start = exp.substring(0, i - 1).lastIndexOf(" ") + 1;
// TODO symb length
int t = exp.substring(i + 1 + 1).indexOf(" ");
int end = i + 2 + (t < 0 ? exp.substring(i + 2).length() : t);
if (Constant.DEBUG)
System.out.println("in evalBase: " + num1 + sym + num2);
String temp = evalBaseMath(num1, sym, num2);
if (Constant.DEBUG)
System.out.println("in evalBase: answer:" + temp);
exp = exp.substring(0, start) + temp + " "
+ (t < 0 ? "" : exp.substring(end + 1));
if (Constant.DEBUG)
System.out.println("in evalBase: exp trans to:" + exp);
i = indexofMulDiv(exp);
}

while (exp.indexOf(" ") != exp.lastIndexOf(" ")) {
int j = exp.indexOf(" ") + 1;
if (Constant.DEBUG)
System.out.println("in evalBase:2 exp=" + exp + ",j=" + j);
String num1 = exp.substring(0, j - 1);
String sym = exp.substring(j, j + 1);
String num2 = wordAfter(exp, j);
if (Constant.DEBUG)
System.out.println("in evalBase:2 " + num1 + sym + num2);
exp = evalBaseMath(num1, sym, num2)
+ exp.substring(j + 1 + 1 + num2.length());
if (Constant.DEBUG)
System.out.println("in evalBase:2 exp trans to:" + exp);
}
return exp.substring(0, exp.length() - 1);

}

private static String evalBaseMath(String num1, String exp, String num2)
throws ExpressionException {
float num_1 = Float.parseFloat(num1);
float num_2 = Float.parseFloat(num2);
if (exp.equals("+")) {
return String.valueOf(num_1 + num_2);
} else if (exp.equals("-")) {
return String.valueOf(num_1 - num_2);
} else if (exp.equals("*")) {
return String.valueOf(num_1 * num_2);
} else if (exp.equals("/")) {
float x = num_1 / num_2;
if (Float.isInfinite(x) || num_2 == 0) {
throw new ExpressionException(ERROR_DIV_ZERO);
}
return String.valueOf(x);
} else {
throw new ExpressionException(ERROR_UNKNOWN);
}
}

// private static String evalBase_x(String exp) throws ExpressionException {
// try {
// if (exp == null || exp.trim().equals("")) {
// throw new ExpressionException(ERROR_EMPTY_EXP);
// }
// Object obj = i.evalBase(exp);
// if (obj == null) {
// throw new ExpressionException(ERROR_EMPTY_VAL);
// }
// String temp = obj.toString();
// if (Constant.DEBUG)
// System.out.println("trans exp: \"" + exp + "\" to \"" + temp
// + "\"");
// return temp;
// } catch (EvalError e) {
// e.printStackTrace();
// throw new ExpressionException(ERROR_UNKNOWN);
// }
// }

private static String evalExpert(String expertStr)
throws ExpressionException {
String[] eps = expertStr.split(" ");
double num1 = Double.parseDouble(eps[0]);
double num2 = Double.parseDouble(eps[2]);
if (eps.length != 3) {
throw new ExpressionException(ERROR_EXPERT);
}
if (eps[1].equals("^")) {
return String.valueOf(Math.pow(num1, num2));
} else if (eps[1].equals("√")) {
double x = 1.0 / num1;
if (Double.isInfinite(x) || num1 == 0) {
throw new ExpressionException(ERROR_INFINITY);
}
return String.valueOf(Math.pow(num2, x));
} else if (eps[1].equals("l")) {
double x = Math.log10(num1);
if (num1 == 0 || x == 0) {
throw new ExpressionException(ERROR_INFINITY);
}
x = Math.log10(num2) / x;
if (Double.isInfinite(x)) {
throw new ExpressionException(ERROR_INFINITY);
}
return String.valueOf(x);
} else if (eps[1].equals("%")) {
if (num2 == 0) {
throw new ExpressionException(ERROR_INFINITY);
}
return String.valueOf(num1 % num2);
}
return null;
}

private static int indexofMulDiv(String exp) {
int i = exp.indexOf("*" + " ");
int j = exp.indexOf("/" + " ");
return i == -1 ? j : (j == -1 ? i : j);
}

/**
* @return minimal not -1 value
*/
private static int indexofExpert(String exp) {
int i = exp.indexOf("^" + " ");
int j = exp.indexOf("√" + " ");
int k = exp.indexOf("l" + " ");
int l = exp.indexOf("%" + " ");
int r = -1;
if (i != -1)
r = i;
if (j != -1)
r = r == -1 ? j : (r < j ? r : j);
if (k != -1)
r = r == -1 ? k : (r < k ? r : k);
if (l != -1)
r = r == -1 ? l : (r < l ? r : l);
return r;
}

private static String format(String exp) {
for (Entry<String, String> entry : Constant.expressionFormats
.entrySet()) {
exp = exp.replaceAll(entry.getKey() + " ", entry.getValue() + " ");
}
return exp;
}

private static String wordAfter(String exp, int idx) {
return wordAfter(exp, idx, 1);
}

private static String wordAfter(String exp, int idx, int count) {
if (idx >= exp.length())
return null;
exp = exp.substring(idx);
if (exp.length() < 1 + count * 2)
return null;// is last
for (int i = 0; i < count; i++)
exp = exp.substring(exp.indexOf(" ") + 1);
return exp.substring(0, exp.indexOf(" "));
}

private static String wordBefore(String exp, int idx) {
if (idx < 2)
return null;
exp = exp.substring(0, idx - 1);// minus 1 space
return exp.substring(exp.lastIndexOf(" ") + 1);
}

public static void main(String[] args) {
String answer = Utility.calculateExpressionValue("( 2 + 9 ) log 0 ");
// if (Constant.DEBUG)
System.out.println(answer);
}



补充一句,比bsh做得多的地方是能计算 ^, √, log等特殊数学运算符,当然实现部分是用Math做得。
阅读更多
换一批

没有更多推荐了,返回首页