摘要:

前幾天要做一個計算數(shù)學(xué)表達(dá)式的題目,本來計劃使用解析表達(dá)式的方法來解析各種數(shù)學(xué)表達(dá)式,然后再動態(tài)計算表達(dá)式的值.后來考慮到這樣編程的任務(wù)很重,時間有限 后來在網(wǎng)上搜搜,看到使用動態(tài)編譯并使用反射機(jī)制 ,這樣計算表達(dá)式的編程就容易多了.

前幾天要做一個計算數(shù)學(xué)表達(dá)式的題目,本來計劃使用解析表達(dá)式的方法來解析各種數(shù)學(xué)表達(dá)式,然后再動態(tài)計算表達(dá)式的值.后來考慮到這樣編程的任務(wù)很重,時間有限 后來在網(wǎng)上搜搜,看到使用動態(tài)編譯并使用反射機(jī)制 ,這樣計算表達(dá)式的編程就容易多了.下面是我這次編程的例子, 請大家看看.


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?}
?

定義一個接口,?用來轉(zhuǎn)換各種數(shù)學(xué)符號為Java類庫中的表達(dá)式.

下面是用來計算的代碼.

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.在輸入超越函數(shù)的時候,變量和數(shù)值用括號擴(kuò)起來?如:sin(x)?而不要寫為?sinx
021??*?3.在兩個數(shù)或者變量相乘時候,不要省略乘號*?如:2*a?不要寫為?2a
022??*?4.在寫冪運(yùn)算的時候,請使用如下格式:?
023??*?利用動態(tài)編譯來計算Simpson積分,使用該方法?編程相對簡單,運(yùn)行效率有點(diǎn)慢.
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???/**主函數(shù)?*/
043???public?static?void?main(String[]?args)?throws?Exception?{
044?????Simpson?sim?=?new?Simpson();
045?????System.out.println("結(jié)果如下:");
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???/**?初始化用戶輸入,為技術(shù)Simpson積分做準(zhǔn)備.?*/
057???private?void?init()?{
058?????Scanner?scanner?=?new?Scanner(System.in);
059?????System.out.println("請輸入函數(shù)表達(dá)式?如?1+sin(a)?+?cos(a)/a?:");
060?????//?String?input?=?scanner.nextLine();
061?????//讀入被積函數(shù)的表達(dá)式
062?????expression?=?scanner.nextLine().trim().toLowerCase();
063?????System.out.println("請輸入變量字符?如?a?:");
064?????//讀入變量字符
065?????variable?=?scanner.nextLine().trim().toLowerCase();
066?????
067?????//處理多元函數(shù)?目前不實(shí)現(xiàn)該功能
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("請輸入積分區(qū)間和結(jié)點(diǎn)數(shù)?如?2?5.4?10?:");
074?????//讀取復(fù)合Simpson公式的積分參數(shù)
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?????//保存中間結(jié)果
086?????double?value1?=?0;
087?????double?value2?=?0;
088?????double?tempValue?=?0;
089?????int?i?=?0;
090?????//?解析輸入的積分參數(shù)值
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)?的函數(shù)值
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????*?得到用戶輸入表達(dá)式轉(zhuǎn)換為Java中的可計算表達(dá)式的函數(shù)
155????*?
@param?ex?輸入的表達(dá)式?如:?1/(1?+?sin(x))?
156????*?
@param?var?表達(dá)式中的變量?如:?x
157????*?
@param?value?變量的取值?如:?4.3
158????*?
@return?Java中可以直接計算的表達(dá)式?如:?1/(1?+?Math.sin(x))
159????
*/

160???private?String?getSourceCode(String?ex,?String?var,?String?value)?{
161?????String?expression?=?ex;
162?????//計算多個變量的函數(shù)的時候使用
163?????
164?????expression?=?expression.replaceAll(var,?value);
165?????
166?????//處理數(shù)學(xué)符號
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?????//?創(chuàng)建一個臨時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?????//?當(dāng)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?????//?動態(tài)構(gòu)造一個類,用于計算
214?????out.write("public?class?"?+?classname?+?"{"
215?????????+?"public?static?double?main1(String[]?args)"?+?"{");
216?????out.write("double?result?=?"?+?code?+?";");
217?????//用于調(diào)試
218?????//out.write("System.out.println(result);");
219?????out.write("return?new?Double(result);");
220?????out.write("}}");
221?????//關(guān)閉文件流
222?????out.flush();
223?????out.close();
224?????//設(shè)置編譯參數(shù)
225?????String[]?args?=?new?String[]?{?"-d",?System.getProperty("user.dir"),
226?????????filename?}
;
227?????//調(diào)試
228?????if?(logger.isLoggable(Level.INFO))?{
229???????logger.info("編譯參數(shù):?"?+?args[0]);
230?????}

231?????//Process?process?=?Runtime.getRuntime().exec("javac?"?+?filename);
232?????int?status?=?Main.compile(args);
233?????//輸出運(yùn)行的狀態(tài)碼.
234?????//????狀態(tài)參數(shù)與對應(yīng)值?
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????*?運(yùn)行程序?如果出現(xiàn)Exception?則不做處理?拋出!
249????*?
@param?file?運(yùn)行的文件名
250????*?
@return?得到的Simpson積分公式的結(jié)果
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?????//當(dāng)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???????//執(zhí)行計算方法?得到計算的結(jié)果
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("請重新運(yùn)行一遍");
272?????}

273?????//返回值
274?????return?tempResult.doubleValue();
275???}

276?
277???/**?調(diào)試函數(shù)*/
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文件來運(yùn)行改程序.(在這里沒有打包為.jar文件)

@echo 注意:
@echo ***********************************************************
@echo * 利用Simpson公式計算積分,在輸入被積公式時候請注意使用 ***
@echo * 如下格式. ***
@echo * 1.只使用圓括號() , 沒有別的括號可以使用.如: ***
@echo * 1/(1+sin(x)) ***
@echo * 2.在輸入超越函數(shù)的時候,變量和數(shù)值用括號擴(kuò)起來 如: ***
@echo * sin(x) 而不要寫為 sinx ***
@echo * 3.在兩個數(shù)或者變量相乘時候,不要省略乘號* 如: ***
@echo * 2*a 不要寫為 2a ***
@echo * 4.在寫冪運(yùn)算的時候,請使用如下格式: ***
@echo * pow(x,y) 代表x的y次冪 不要使用其他符號 ***
@echo * 5.絕對值請用如下符號表示: ***
@echo * fabs(x) 代表x的絕對值 ***
@echo * 6.指數(shù)函數(shù)請用exp表示 如:exp(x) ***
@echo * 7.對數(shù)函數(shù)請用log(x)表示, 該處對數(shù)是指底為10的對數(shù), ***
@echo * 計算不是以10為底的對數(shù)時候請轉(zhuǎn)換為10為底的對數(shù) ***
@echo * 8.變量字符請不要與函數(shù)中的其他字符重合,如 如果使用了 ***
@echo * sin 函數(shù)請 不要用 s i 或者n做為變量,否則在解析 ***
@echo * 表達(dá)式時候 會出錯 ^_^
@echo ***********************************************************

@Rem 在編譯源文件時候 要使用下面的命令 把rem 刪除即可 注意 由于文件中用到了tools.jar中
@rem 的命令 所有在編譯的時候 用適當(dāng)?shù)腸lasspath 替換下面的 tools.jar的路徑 運(yùn)行的時候一樣

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

@rem 注意更改此處的tools.jar的路徑 為你當(dāng)前系統(tǒng)的正確路徑
@java -cp ".;D:\Program Files\Java\jdk1.5.0_03\lib\tools.jar" Simpson

@Pause

 

這樣就可以了.

說明:

使用該方法來計算本程序,由于要多次動態(tài)產(chǎn)生計算源代碼,并且編譯 在性能上會有很大損失. 要是在項(xiàng)目中不經(jīng)常計算表達(dá)式 使用該方法可以減輕編程的負(fù)擔(dān).要是象上面那樣 要多次計算的話,使用該方法是很值得考慮的.