站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题利用Java动态编译计算数学表达式

利用Java动态编译计算数学表达式

摘要: 利用Java动态编译计算数学表达式
内容: 前几天要做一个计算数学表达式的题目,本来计划使用解析表达式的方法来解析各种数学表达式,然后再动态计算表达式的值.后来考虑到这样编程的任务很重,时间有限 后来在网上搜搜,看到使用动态编译并使用反射机制 ,这样计算表达式的编程就容易多了.下面是我这次编程的例子, 请大家看看.

01 /*
02 * Created on 2006-3-8
03 * @author icerain 我的Blog: http://blog.matrix.org.cn/page/icess
04 */
05
06 public interface IOperator {
07 String SIN = "sin";
08 String COS = "cos";
09 String TAN = "tan";
10 String ASIN = "asin";
11 String ACOS = "acos";
12 String ATAN = "atan";
13 String EXP = "exp";
14 String LOG = "log";
15 String POW = "pow";
16 String SQRT = "sqrt";
17 String FABS = "fabs";
18 String MINUS = "minus";
19
20 String J_SIN = "Math.sin";
21 String J_COS = "Math.cos";
22 String J_TAN = "Math.tan";
23 String J_ASIN = "Math.asin";
24 String J_ACOS = "Math.acos";
25 String J_ATAN = "Math.atan";
26 String J_EXP = "Math.exp";
27 String J_LOG = "Math.log10";
28 String J_POW = "Math.pow";
29 String J_SQRT = "Math.sqrt";
30 String J_FABS = "Math.abs";
31
32 }

定义一个接口, 用来转换各种数学符号为Java类库中的表达式.

下面是用来计算的代码.

001 /*
002 * Created on 2006-3-7
003 * @author icerain 我的Blog: http://blog.matrix.org.cn/page/icess
004 */
005 //package hust.icess.simpson;
006
007
008 import java.util.logging.Level;
009
010 import java.io.*;
011 import java.lang.reflect.Method;
012 import java.util.Scanner;
013 import java.util.logging.Logger;
014
015
016 import com.sun.tools.javac.*;
017 /**
018 * 利用Simpson公式计算积分,在输入被积公式时候请注意使用如下格式.
019 * 1.只使用圆括号() , 没有别的括号可以使用.如: 1/(1+sin(x))
020 * 2.在输入超越函数的时候,变量和数值用括号扩起来 如:sin(x) 而不要写为 sinx
021 * 3.在两个数或者变量相乘时候,不要省略乘号* 如:2*a 不要写为 2a
022 * 4.在写幂运算的时候,请使用如下格式:
023 * 利用动态编译来计算Simpson积分,使用该方法 编程相对简单,运行效率有点慢.
024 * @author icerain
025 *
026 */
027 public class Simpson implements IOperator {
028 /**
029 * Logger for this class
030 */
031 private static final Logger logger = Logger.getLogger(Simpson.class
032 .getName());
033
034 private String expression = null;
035
036 private String variable = null;
037
038 private String[] variableValue = new String[3];
039
040 // private static Main javac = new Main();
041
042 /**主函数 */
043 public static void main(String[] args) throws Exception {
044 Simpson sim = new Simpson();
045 System.out.println("结果如下:");
046 System.out.print(sim.getSimpsonValue());
047 System.exit(0);
048
049 }
050
051 public Simpson() {
052 logger.setLevel(Level.WARNING);
053 init();
054 }
055
056 /** 初始化用户输入,为技术Simpson积分做准备. */
057 private void init() {
058 Scanner scanner = new Scanner(System.in);
059 System.out.println("请输入函数表达式 如 1+sin(a) + cos(a)/a :");
060 // String input = scanner.nextLine();
061 //读入被积函数的表达式
062 expression = scanner.nextLine().trim().toLowerCase();
063 System.out.println("请输入变量字符 如 a :");
064 //读入变量字符
065 variable = scanner.nextLine().trim().toLowerCase();
066
067 //处理多元函数 目前不实现该功能
068 // String[] tempVars = tempVar.split(" ");
069 // for(int i = 0; i < tempVars.length; i ++) {
070 // variable[i] = tempVars[i];
071 // }
072
073 System.out.println("请输入积分区间和结点数 如 2 5.4 10 :");
074 //读取复合Simpson公式的积分参数
075 String tempValue = scanner.nextLine().trim();
076 String[] tempValues = tempValue.split(" ");
077 for (int i = 0; i < tempValues.length; i++) {
078 variableValue[i] = tempValues[i];
079 }
080
081 }
082
083 /** 计算 Simpson积分的值*/
084 public double getSimpsonValue() {
085 //保存中间结果
086 double value1 = 0;
087 double value2 = 0;
088 double tempValue = 0;
089 int i = 0;
090 // 解析输入的积分参数值
091 int n = Integer.parseInt(variableValue[2]);
092 double a = Double.parseDouble(variableValue[0]);
093 double b = Double.parseDouble(variableValue[1]);
094 double h = (b - a) / n;
095 //计算value1
096 for (i = 0; i < n; i++) {
097 tempValue = a + (i + 0.5) * h;
098 String code = getSourceCode(expression, getVariable(), Double
099 .toString(tempValue));
100 try {
101 value1 += run(compile(code));
102 } catch (Exception e) {
103 // TODO Auto-generated catch block
104 e.printStackTrace();
105
106 if (logger.isLoggable(Level.INFO)) {
107 logger.info("something is wrong");
108 }
109 }
110 }
111 //计算value2
112 for (i = 1; i < n; i++) {
113 tempValue = a + i * h;
114 String code = getSourceCode(expression, getVariable(), Double
115 .toString(tempValue));
116 try {
117 value2 += run(compile(code));
118 } catch (Exception e) {
119 // TODO Auto-generated catch block
120 e.printStackTrace();
121 if (logger.isLoggable(Level.INFO)) {
122 logger.info("something is wrong");
123 }
124 }
125 }
126
127 //计算f(a) f(b) 的函数值
128 double valueA = getFunctionValue(a);
129 double valueB = getFunctionValue(b);
130 //计算Simpson公式的值
131 double resultValue = (valueA + valueB + 4 * value1 + 2 * value2) * h / 6;
132
133 return resultValue;
134 }
135
136 //计算F(a) 的值
137 private double getFunctionValue(double varValue) {
138 String code = getSourceCode(expression, getVariable(), Double
139 .toString(varValue));
140 double result = 0;
141 try {
142 result = run(compile(code));
143 } catch (Exception e) {
144 // TODO Auto-generated catch block
145 e.printStackTrace();
146 if (logger.isLoggable(Level.INFO)) {
147 logger.info("something is wrong");
148 }
149 }
150 return result;
151 }
152
153 /**
154 * 得到用户输入表达式转换为Java中的可计算表达式的函数
155 * @param ex 输入的表达式 如: 1/(1 + sin(x))
156 * @param var 表达式中的变量 如: x
157 * @param value 变量的取值 如: 4.3
158 * @return Java中可以直接计算的表达式 如: 1/(1 + Math.sin(x))
159 */
160 private String getSourceCode(String ex, String var, String value) {
161 String expression = ex;
162 //计算多个变量的函数的时候使用
163
164 expression = expression.replaceAll(var, value);
165
166 //处理数学符号
167 if (expression.contains(SIN)) {
168 expression = expression.replaceAll(SIN, J_SIN);
169 } else if (expression.contains(COS)) {
170 expression = expression.replaceAll(COS, J_COS);
171 } else if (expression.contains(TAN)) {
172 expression = expression.replaceAll(TAN, J_TAN);
173 } else if (expression.contains(ASIN)) {
174 expression = expression.replaceAll(ASIN, J_ASIN);
175 } else if (expression.contains(ACOS)) {
176 expression = expression.replaceAll(ACOS, J_ACOS);
177 } else if (expression.contains(ATAN)) {
178 expression = expression.replaceAll(ATAN, J_ATAN);
179 } else if (expression.contains(EXP)) {
180 expression = expression.replaceAll(EXP, J_EXP);
181 } else if (expression.contains(LOG)) {
182 expression = expression.replaceAll(LOG, J_LOG);
183 } else if (expression.contains(POW)) {
184 expression = expression.replaceAll(POW, J_POW);
185 } else if (expression.contains(SQRT)) {
186 expression = expression.replaceAll(SQRT, J_SQRT);
187 } else if (expression.contains(FABS)) {
188 expression = expression.replaceAll(FABS, J_FABS);
189 }
190
191 return expression;
192 }
193
194 /** 编译JavaCode,返回java文件*/
195 private synchronized File compile(String code) throws Exception {
196 File file;
197 // 创建一个临时java源文件
198 file = File.createTempFile("JavaRuntime", ".java", new File(System
199 .getProperty("user.dir")));
200 if (logger.isLoggable(Level.INFO)) {
201 logger.info(System.getProperty("user.dir"));
202 }
203 // 当Jvm 退出时 删除该文件
204 file.deleteOnExit();
205 // 得到文件名和类名
206 String filename = file.getName();
207 if (logger.isLoggable(Level.INFO)) {
208 logger.info("FileName: " + filename);
209 }
210 String classname = getClassName(filename);
211 // 将代码输出到源代码文件中
212 PrintWriter out = new PrintWriter(new FileOutputStream(file));
213 // 动态构造一个类,用于计算
214 out.write("public class " + classname + "{"
215 + "public static double main1(String[] args)" + "{");
216 out.write("double result = " + code + ";");
217 //用于调试
218 //out.write("System.out.println(result);");
219 out.write("return new Double(result);");
220 out.write("}}");
221 //关闭文件流
222 out.flush();
223 out.close();
224 //设置编译参数
225 String[] args = new String[] { "-d", System.getProperty("user.dir"),
226 filename };
227 //调试
228 if (logger.isLoggable(Level.INFO)) {
229 logger.info("编译参数: " + args[0]);
230 }
231 //Process process = Runtime.getRuntime().exec("javac " + filename);
232 int status = Main.compile(args);
233 //输出运行的状态码.
234 // 状态参数与对应值
235 //   EXIT_OK 0
236 //   EXIT_ERROR 1
237 //   EXIT_CMDERR 2
238 //   EXIT_SYSERR 3
239 //   EXIT_ABNORMAL 4
240 if (logger.isLoggable(Level.INFO)) {
241 logger.info("Compile Status: " + status);
242 }
243 //System.out.println(process.getOutputStream().toString());
244 return file;
245 }
246
247 /**
248 * 运行程序 如果出现Exception 则不做处理 抛出!
249 * @param file 运行的文件名
250 * @return 得到的Simpson积分公式的结果
251 * @throws Exception 抛出Exception 不作处理
252 */
253 private synchronized double run(File file) throws Exception {
254 String filename = file.getName();
255 String classname = getClassName(filename);
256 Double tempResult = null;
257 // System.out.println("class Name: " +classname);
258 //当Jvm 退出时候 删除生成的临时文件
259 new File(file.getParent(), classname + ".class").deleteOnExit();
260 try {
261 Class cls = Class.forName(classname);
262 //System.out.println("run........");
263 // 映射main1方法
264 Method calculate = cls
265 .getMethod("main1", new Class[] { String[].class });
266 //执行计算方法 得到计算的结果
267 tempResult = (Double) calculate.invoke(null,
268 new Object[] { new String[0] });
269 } catch (SecurityException se) {
270 System.out.println("something is wrong !!!!");
271 System.out.println("请重新运行一遍");
272 }
273 //返回值
274 return tempResult.doubleValue();
275 }
276
277 /** 调试函数*/
278 // private void debug(String msg) {
279 // System.err.println(msg);
280 // }
281
282 /** 得到类的名字 */
283 private String getClassName(String filename) {
284 return filename.substring(0, filename.length() - 5);
285 }
286
287
288 //getter and setter
289 public String getExpression() {
290 return expression;
291 }
292
293 public void setExpression(String expression) {
294 this.expression = expression;
295 }
296
297 public String getVariable() {
298 return variable;
299 }
300
301 public void setVariable(String variable) {
302 this.variable = variable;
303 }
304
305 public String[] getVariableValue() {
306 return variableValue;
307 }
308
309 public void setVariableValue(String[] variableValue) {
310 this.variableValue = variableValue;
311 }
312 }

这样就可以用来计算了.

下面编写一个.bat文件来运行改程序.(在这里没有打包为.jar文件)

@echo 注意:
@echo ***********************************************************
@echo * 利用Simpson公式计算积分,在输入被积公式时候请注意使用 ***
@echo * 如下格式. ***
@echo * 1.只使用圆括号() , 没有别的括号可以使用.如: ***
@echo * 1/(1+sin(x)) ***
@echo * 2.在输入超越函数的时候,变量和数值用括号扩起来 如: ***
@echo * sin(x) 而不要写为 sinx ***
@echo * 3.在两个数或者变量相乘时候,不要省略乘号* 如: ***
@echo * 2*a 不要写为 2a ***
@echo * 4.在写幂运算的时候,请使用如下格式: ***
@echo * pow(x,y) 代表x的y次幂 不要使用其他符号 ***
@echo * 5.绝对值请用如下符号表示: ***
@echo * fabs(x) 代表x的绝对值 ***
@echo * 6.指数函数请用exp表示 如:exp(x) ***
@echo * 7.对数函数请用log(x)表示, 该处对数是指底为10的对数, ***
@echo * 计算不是以10为底的对数时候请转换为10为底的对数 ***
@echo * 8.变量字符请不要与函数中的其他字符重合,如 如果使用了 ***
@echo * sin 函数请 不要用 s i 或者n做为变量,否则在解析 ***
@echo * 表达式时候 会出错 ^_^
@echo ***********************************************************

@Rem 在编译源文件时候 要使用下面的命令 把rem 删除即可 注意 由于文件中用到了tools.jar中
@rem 的命令 所有在编译的时候 用适当的classpath 替换下面的 tools.jar的路径 运行的时候一样

@rem javac -classpath ".;D:\Program Files\Java\jdk1.5.0_03\lib\tools.jar;%CLASSPATH%" Simpson.java %1

@rem 注意更改此处的tools.jar的路径 为你当前系统的正确路径
@java -cp ".;D:\Program Files\Java\jdk1.5.0_03\lib\tools.jar" Simpson

@Pause

 

这样就可以了.

说明:

使用该方法来计算本程序,由于要多次动态产生计算源代码,并且编译 在性能上会有很大损失. 要是在项目中不经常计算表达式 使用该方法可以减轻编程的负担.要是象上面那样 要多次计算的话,使用该方法是很值得考虑的.


Java, java, J2SE, j2se, J2EE, j2ee, J2ME, j2me, ejb, ejb3, JBOSS, jboss, spring, hibernate, jdo, struts, webwork, ajax, AJAX, mysql, MySQL, Oracle, Weblogic, Websphere, scjp, scjd 前几天要做一个计算数学表达式的题目,本来计划使用解析表达式的方法来解析各种数学表达式,然后再动态计算表达式的值.后来考虑到这样编程的任务很重,时间有限 后来在网上搜搜,看到使用动态编译并使用反射机制 ,这样计算表达式的编程就容易多了.下面是我这次编程的例子, 请大家看看.

01 /*
02 * Created on 2006-3-8
↑返回目录
前一篇: JDK1.4和JDK1.5在linux下的中文显示配置
后一篇: 基于Java 开发QuickTime 程序