摘要:
前幾天要做一個計算數(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).要是象上面那樣 要多次計算的話,使用該方法是很值得考慮的.