为了替换项目里面的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做得。

你可能感兴趣的:(算法,J#)