周末,看關(guān)于專家系統(tǒng)方面的書,其中有關(guān)于規(guī)則方面的內(nèi)容,忽然就想,能不能模仿人的學習方式來提升計算機程序的計算能力呢?
試想,一個小孩子,他一開始什么也不會,首先,你要告訴他什么是數(shù)字,然后告訴他什么是加、減;然后告訴他什么是乘、除,還要告訴他有乘、除要先計算乘除,然后又引入了括號說,有括號永遠要先計算括號。如此,隨著告訴他的技能越多,他的解題能力也就越強。
于是就想著試驗一下。
第一步,教計算機學習什么是數(shù)字。
下面的正則表達式,就是告訴“孩子”,數(shù)字就是前面可能有“-”號,當然也可能沒有,接下來連續(xù)的數(shù)字0-9,組成的數(shù)字,后面可能還會有小數(shù)點開始加一堆0-9的數(shù)字,當然沒有也沒有關(guān)系。如此,它就算懂得認數(shù)字了。
public final class MathNumber {
private MathNumber() {
}
public static String numberPattern = "[-]?[0-9]+([.][0-9]*)?";
public static Pattern pattern = Pattern.compile(numberPattern);
public static Matcher match(String string) {
Matcher match = pattern.matcher(string);
if (match.find()) {
return match;
}
throw new RuntimeException(string + " is not a number.");
}
}
第二步就是告訴“孩子”,計算數(shù)學題的過程。
如果兩邊有空格就忽略它,然后呢,看看是不是已經(jīng)是一個數(shù)字了,如果已經(jīng)是一個數(shù)字,那說明就算出結(jié)果了。如果不是,就從最高優(yōu)先級找起,如果找就就計算。如果找不到,說明這個式子有問題,不是一個合法的數(shù)學式子。
public static String eval(String string) {
string = string.trim();
while (!isMathNumber(string)) {// 同一優(yōu)先級的哪個先找到算哪個
System.out.println("求解算式:" + string);
boolean found = false;
for (MathInterface math : mathList) {
Matcher matcher = math.match(string);
if (matcher.find()) {
String exp = matcher.group();
String sig = "";
if (exp.charAt(0) == '-' && matcher.start() != 0) {// 如果不是第一個數(shù)字,-號只能當運算符
sig = "+";
}
System.out.println("發(fā)現(xiàn)算式:" + exp);
String evalResult = math.eval(exp);
string = string.substring(0, matcher.start()) + sig
+ evalResult + string.substring(matcher.end());
System.out.println(exp + "計算結(jié)果為:" + evalResult + ",代回原式");
found = true;
break;
}
}
if (!found) {
throw new RuntimeException(string + " 不是合法的數(shù)學表達式");
}
}
return string;
}
從現(xiàn)在開始,這孩子已經(jīng)會解題思路了,不過他還是啥也不懂,他還不知道啥是加,減、乘、除啥的,沒有辦法,孩子笨,只要多教他了。
下面就教他如何計算,加、減、乘、除、余、括號、指數(shù)。
addMathExpression(new Add());
addMathExpression(new Subtract());
addMathExpression(new Multiply());
addMathExpression(new Devide());
addMathExpression(new Minus());
addMathExpression(new Factorial());
addMathExpression(new Remainder());
addMathExpression(new Bracket());
addMathExpression(new Power());
Collections.sort(mathList, new MathComparator());
由于大同小異,就里就只貼出來加法和括號的實現(xiàn)方式。
加法實現(xiàn),它的優(yōu)先級是1,它是由兩個數(shù)字中間加一個“+”號構(gòu)成,數(shù)字和加號前面的空格沒用,不用管它。計算的時候呢,就是用加的方式把兩個數(shù)字加起來,這一點計算機比人強,呵呵,告訴他怎么加永遠不會錯的。而且理解起加減乘除先天有優(yōu)勢。
public class Add implements MathInterface {
static String plusPattern = BLANK + MathNumber.numberPattern + BLANK
+ "[+]{1}" + BLANK + MathNumber.numberPattern + BLANK;
static Pattern pattern = Pattern.compile(plusPattern);
static Pattern plus = Pattern.compile(BLANK + "\\+");
@Override
public Matcher match(String string) {
return pattern.matcher(string);
}
@Override
public int priority() {
return 1;
}
@Override
public String eval(String expression) {
Matcher a = MathNumber.pattern.matcher(expression);
if (a.find()) {
expression = expression.substring(a.end());
}
Matcher p = plus.matcher(expression);
if (p.find()) {
expression = expression.substring(p.end());
}
Matcher b = MathNumber.pattern.matcher(expression);
if (b.find()) {
}
return new BigDecimal(a.group()).add(new BigDecimal(b.group()))
.toString();
}
}
接下來是括號,括號的優(yōu)先級是最大啦,只要有它就應該先計算。當然,要先計算最內(nèi)層的括號中的內(nèi)容。括號中的內(nèi)容,計算的時候,可以先拉出來,不用管外面的內(nèi)容,計算好了,放回去就可以了。
public class Bracket implements MathInterface {
static String bracketPattern = BLANK + "[(]{1}[^(]*?[)]" + BLANK;
static Pattern pattern = Pattern.compile(bracketPattern);
@Override
public Matcher match(String string) {
return pattern.matcher(string);
}
@Override
public int priority() {
return Integer.MAX_VALUE;
}
@Override
public String eval(String expression) {
expression = expression.trim();
return MathEvaluation.eval(expression.substring(1,
expression.length() - 1));
}
}
到目前為止,我們的程序“寶寶”已經(jīng)學會數(shù)學計算了,出個題讓伊試試。
public static void main(String[] args) {
String string = "1+2^(4/2)+5%2";
System.out.println("結(jié)果是 :" + MathEvaluation.eval(string));
}
程序?qū)殞毜淖鲱}過程如下:
求解算式:1+2^(4/2)+5%2
發(fā)現(xiàn)算式:(4/2)
求解算式:4/2
發(fā)現(xiàn)算式:4/2
4/2計算結(jié)果為:2.00,代回原式
(4/2)計算結(jié)果為:2.00,代回原式
求解算式:1+2^2.00+5%2
發(fā)現(xiàn)算式:2^2.00
2^2.00計算結(jié)果為:4,代回原式
求解算式:1+4+5%2
發(fā)現(xiàn)算式:5%2
5%2計算結(jié)果為:1,代回原式
求解算式:1+4+1
發(fā)現(xiàn)算式:1+4
1+4計算結(jié)果為:5,代回原式
求解算式:5+1
發(fā)現(xiàn)算式:5+1
5+1計算結(jié)果為:6,代回原式
結(jié)果是 :6
呵呵,程序?qū)殞毜淖鲱}過程和人的做題過程非常一致,而且程序?qū)崿F(xiàn)也非常簡單易懂。神馬編譯原理,神馬中綴表達式都用不上。(執(zhí)行效率與其它算法比較不一定高,僅用于驗證通過規(guī)則讓程序的處理能力增強,由于沒有進行深入測試,正則表達式和程序邏輯是否寫得嚴密沒有經(jīng)過深入驗證)
其實程序雖然很簡單,但是,實際上已經(jīng)是一個簡單的規(guī)則引擎的雛形。
首先,他加載了許多的業(yè)務處理規(guī)則,加,減,乘,除,插號,指數(shù),余數(shù)等等。
第二,他的業(yè)務規(guī)則是可以不斷進行擴展的。
第三,只要給出事實,最后,他通過規(guī)則的不斷應用,最后會導出結(jié)果,要么是正確的結(jié)果,要么說給出的事實是錯誤的。
需要源碼的童鞋請到GIT上直接獲取代碼。