實現四則運算一般都是利用自動機理論,對字符逐個讀取,然後判斷應處的狀態,最後將自動機優化實現程序。
只是突然想起有另一種計算方式,不知是否有前輩已經寫出,只是怕自己會突然忘記,因此記錄下來,供以後使用。
對於一個簡單的加減法運算來說(拋去有正數或負數的存在,因爲負號與減號容易混淆),符號總是比數據少一個,例如
/*對於一個四則元算字符串:A+B-C 來說,若是以符號爲分隔將數和運算符分開,以向量來存儲,那麼結果爲:*/
number:A B C
operator:+ -
此時怎麼做應該很清楚了吧,對向量operator取第一個成員,此時得到了一個“+”號,那麼只要取number中的兩個數,然後讓兩個數相加(假如說得到了D,此時要根據運算符號來操作兩個數),放入number第一個成員位置,再移除operator第一個成員,移除number第二個成員,例如:
number.setElementAt(number.firstElement()+number.get(1),0);
number.remove(1);
operator.remove(0);
/*此時兩個向量中剩下的內容是什麼:
number:D C
operator:-
看到了吧,繼續上次的運算,直到operator中爲空,此時number中必定還剩下一個成員,那就是最終的結果,下面貼出完整步驟:
while(!operator.isEmpty()){
char ch=operator.firstElement();
if(ch=='+'){
number.setElementAt(number.firstElement()+number.get(1),0);
}else if(ch=='-'){
number.setElementAt(number.firstElement()-number.get(1),0);
}
number.remove(1);
operator.remove(0);
}
return number.firstElement().toString();
看到這大家估計會嗤之以鼻,說這玩意早幾百年都懂了,還寫這幹啥,當然,沒有哪個運算只有加減法,還不能帶符號的,接下來就進行加減法的進階——乘除法。
別笑!
容我先貼出完整的程序來,然後再分析:
protected static String account_second(Vector<Float> number,Vector<Character> operator){
/*對於靜態方法account_second來說,呃,這個函數名字很讓我糾結,不過不想去改代碼了,number代表字符串分割後數的集合,operator相當於分割後操作數的集合*/
try {
int k=0;
while(operator.size()>k){
char ch=operator.get(k);
if(operator.get(k)=='*'){
number.setElementAt(number.get(k) * number.get(k+1), k);
}
else if(ch=='%'){
number.setElementAt(number.get(k)%number.get(k+1),k);
}
else if(ch=='/'){
number.setElementAt(number.get(k)/number.get(k+1),k);
}
//碰到“高級”運算直接操作,此時都是二目運算符,因此不會有問題。
else if(ch=='+'||ch=='-') {
k++;
continue;
}
//碰到“低級”運算符要讓位,這裏只進行高級運算。
number.remove(k+1);
operator.remove(k);
}
while(!operator.isEmpty()){
char ch=operator.firstElement();
if(ch=='+'){
number.setElementAt(number.firstElement()+number.get(1),0);
}else if(ch=='-'){
number.setElementAt(number.firstElement()-number.get(1),0);
}
number.remove(1);
operator.remove(0);
}
return number.firstElement().toString();
//這部分不解釋,就是之前貼過的代碼。
} catch(Exception e){
return "input error";
}
//我非常認真的提醒大家,try catch是精髓,它可以保證:
//咳,保證程序不小心有問題也不會檢查不出來。
}
看到這你估計就明白爲什麼我先貼代碼了,沒錯,按照四則運算的規矩,乘除先行,這裏就是先進行了乘除法的運算,直到operator中沒有乘除取模爲止,若是中間遇到加減符號,直接隔過就行。怎麼樣,簡單吧。
或許對此你仍然不屑一顧,沒有括號的四則運算能叫運算麼?
對,你說的對,這程序若到此爲止,我也不好意思貼出來了,簡直是打我老師的臉。下面進行乘除進階——括號運算。
其實這部分也沒有什麼大問題,只是我思維偏向於混亂,因此這部分的代碼很垃圾,不過總算把功能實現了,如果誰有更爲精準簡潔的代碼,請務必不要告訴我,我怕打臉。
好了,下面還是貼代碼:
public static String account(String string) {
boolean flag=true;
try {
Vector<Float> number=new Vector<Float>();
Vector<Character> operator=new Vector<Character>();
//定義存儲運算符和操作數的向量。
String strings[] = string.split("\\+|-|\\*|%|/|\\(|\\)");/*此處是將四則運算字符串以操作符爲界限進行分割,切記加號乘號以及括號匹配時候是需要轉義的,具體內容請自學正則表達式。“+,-,*,/,%,(,)”*/
for (String i : strings) {
if(!i.equals("")) {
number.add(Float.parseFloat(i));
}
}
//將分割後得到的字符串轉化爲float類型存在number裏。
char chars[] = string.toCharArray();
int num[]=new int[number.size()];
int k=0;
for (Character i : chars) {
if (i == '+' || i == '-' || i == '*' || i == '/' || i == '%') {
operator.add(i);
k++;
}else if(i=='('){
num[k]++;
}else if(i==')'){
num[k]--;
}
}
/*這部分估計你傻眼了吧,其實很簡單,首先對字符串數組化得到操作符,這過程中肯定會碰到括號,因此新建一個與number對應的數組num,裏面記錄括號的位置,碰到一個左括號“(”,就讓對應number所在位置的num加一,碰到一個“)”,就讓對應number位置的num減一,因爲括號的匹配性,查找完之後,num數組中所有數加起來應該還是零。*/
/*不知你發現沒有,此時此刻operator中數據個數還是比number中數據個數少一,一定要記住,這纔是這種算法存在的基礎,要不然天曉得自己應該操作哪個數*/
flag=true;
while(flag){
for(int i=0;i<num.length;i++){
if(num[i]<0){
num[i]+=1;//左括號值爲1,右括號值爲-1;
flag=!flag;
for(int j=i-1;j>=0;j--){
if(num[j]>0){
num[j]-=1;
num[j]=num[j]+num[i];
if(i+1!=num.length){
for(int t=i+1;t<num.length;t++){
num[t-(i-j)]=num[t];
}
}
for(int t=num.length-(i-j);t<num.length;t++){
num[t]=0;
}//將數組中括號匹配到位
Vector<Float> new_number=new Vector<Float>();
Vector<Character> new_operator=new Vector<Character>();
for(int p=j;p<=i-1;p++){
new_number.add(number.get(p));
new_operator.add(operator.get(p));
}
new_number.add(number.get(i));
String str=account_second(new_number,new_operator);
if(!str.equals("input error")){
number.setElementAt(Float.parseFloat(str),i);
}
else{
return "input error";
}
//將括號中數與符號單獨提出,新建對應的向量,然後利用second-account求出值,再將新值
//放入原來向量,直到沒有括號爲止。
for(int m=j;m<i;i--){
number.remove(j);
operator.remove(j);
}
j=-1;i=num.length;
}
}
}
}
flag=!flag;
}
return account_second(number,operator);
} catch (Exception e) {
return "error";
}
}
後面有很長一部分沒有註釋,實在是我的算法太噁心,我自己都不好意思說明白,我只說一下大致要實現的功能:
講解過的部分經過操作應該會得到三個集合,operator,number,num;
對於一個運算式 (7+(5-3))*2 來說,這三部分內容分別如下:
number:7 5 3 2
num: 1 1 -2 0
operator:+ - *
就是這樣,括號的位置記錄在與之接近的操作數上,且不會有單個括號同時接近兩個操作數。
根據四則運算法則,有括號先計算括號裏面的內容,有多個括號呢?那就應該先從左到右查找第一個右括號“)”,然後從第一個右括號開始,向左查找第一個左括號“(”,裏面的部分就是你要先計算的。
括號裏面的內容肯定不含括號了,那就調用使用之前寫的計算乘除加減的方法,將得到的數值寫入number中,然後將括號位置對應的num中數字變化,
還要移除已經使用過的operator中的操作符,移除運算過的number中的操作數,直到num中數字全爲零,最後會剩下沒有括號干擾的number和operator,這下就好辦了吧。
好了以上就是四則運算的java實現,是不是有種噁心的感覺?沒關係,我們接着看,正數和負數的問題還沒解決呢。
好吧,其實最後的問題很好解決,在開始分割字符串之前檢查一下就好了,如果發現有減號出現並且該減號左端不是數字,那麼~~~那麼就在減號前面加個零“0”好了,不要嘗試去直接修改operator中對應的值,那樣會很蠢。這樣得到的字符串不就很規範了麼。自己寫的實現代碼如下:
public static String addChar(String string,int i,boolean b,String str){
if(b){
String frontPart = string.substring(0, i);
String backPart = string.substring(i);
string = frontPart + str + backPart;
}else{
String frontPart=string.substring(0,i+1);
String backPart = string.substring(i+1);
string=frontPart+str+backPart;
}
return string;
}
while(flag) {
flag=!flag;
string=string.replace(" ","");
char ch[] = string.toCharArray();
for (int i = 0; i < ch.length; i++) {
if(ch[i] == '-' || ch[i] == '+') {
if (i == 0 || (i != 0 && ch[i - 1] == '(')) {
string=addChar(string,i,true,"0");
flag =!flag;
break;
}
if(i==ch.length-1||(i!=ch.length-1&&ch[i+1]==')')){
string=addChar(string,i,false,"0");
flag=!flag;
break;
}
}
if(i!=0&&ch[i]=='('&&'0'<=ch[i-1]&&ch[i-1]<='9'){
string=addChar(string,i,true,"*");
flag=!flag;
break;
}
if(i!=ch.length-1&&ch[i]==')'&&'0'<=ch[i+1]&&ch[i+1]<='9'){
string=addChar(string,i,false,"*");
flag=!flag;
break;
}
}
}
這段代碼不單單是解決了負號正號存在的問題,還可以去除字符串空格,以及很多的錯誤輸入問題,如”1+3+”,這明顯是輸入錯誤,而我們可以將其處理,比如變成“1+3+0”,或者“1+3”;還有省略乘號的問題,如“(2+7)4”,我們將其變爲“(2+7)*4”,這樣是不是就人性化多了呢?
當然,這種想法還可以繼續延伸,比如階乘符號怎麼算,次方怎麼算,根號怎麼算,這些都可以根據運算優先級來得到解決。
好了,終於到結尾了,想吐的同學可以吐了,這是我之前自己鼓搗安卓計算器的時候整的,最後時候計算器也能用,因此代碼應該沒問題,但不排除我粘貼複製出手抖出錯,整個計算過程完整代碼附上:
import java.util.*;
/**
* Created by wang on 2015/10/6.
*/
public class Account{
public static String addChar(String string,int i,boolean b,String str){
if(b){
String frontPart = string.substring(0, i);
String backPart = string.substring(i);
string = frontPart + str + backPart;
}else{
String frontPart=string.substring(0,i+1);
String backPart = string.substring(i+1);
string=frontPart+str+backPart;
}
return string;
}
public static String account(String string) {
boolean flag=true;
while(flag) {
flag=!flag;
string=string.replace(" ","");
char ch[] = string.toCharArray();
for (int i = 0; i < ch.length; i++) {
if(ch[i] == '-' || ch[i] == '+') {
if (i == 0 || (i != 0 && ch[i - 1] == '(')) {
string=addChar(string,i,true,"0");
flag =!flag;
break;
}
if(i==ch.length-1||(i!=ch.length-1&&ch[i+1]==')')){
string=addChar(string,i,false,"0");
flag=!flag;
break;
}
}
if(i!=0&&ch[i]=='('&&'0'<=ch[i-1]&&ch[i-1]<='9'){
string=addChar(string,i,true,"*");
flag=!flag;
break;
}
if(i!=ch.length-1&&ch[i]==')'&&'0'<=ch[i+1]&&ch[i+1]<='9'){
string=addChar(string,i,false,"*");
flag=!flag;
break;
}
}
}
try {
Vector<Float> number=new Vector<Float>();
Vector<Character> operator=new Vector<Character>();
String strings[] = string.split("\\+|-|\\*|%|/|c|s|\\(|\\)");
for (String i : strings) {
if(!i.equals("")) {
number.add(Float.parseFloat(i));
}
}
char chars[] = string.toCharArray();
int num[]=new int[number.size()];
int k=0;
for (Character i : chars) {
if (i == '+' || i == '-' || i == '*' || i == '/' || i == '%') {
operator.add(i);
k++;
}else if(i=='('){
num[k]++;
}else if(i==')'){
num[k]--;
}
}
flag=true;
while(flag){
for(int i=0;i<num.length;i++){
if(num[i]<0){
num[i]+=1;//左括號值爲1,右括號值爲-1;
flag=!flag;
for(int j=i-1;j>=0;j--){
if(num[j]>0){
num[j]-=1;
num[j]=num[j]+num[i];
if(i+1!=num.length){
for(int t=i+1;t<num.length;t++){
num[t-(i-j)]=num[t];
}
}
for(int t=num.length-(i-j);t<num.length;t++){
num[t]=0;
}//將數組中括號匹配到位
Vector<Float> new_number=new Vector<Float>();
Vector<Character> new_operator=new Vector<Character>();
for(int p=j;p<=i-1;p++){
new_number.add(number.get(p));
new_operator.add(operator.get(p));
}
new_number.add(number.get(i));
String str=account_second(new_number,new_operator);
if(!str.equals("input error")){
number.setElementAt(Float.parseFloat(str),i);
}
else{
return "input error";
}
//將括號中數與符號單獨提出,新建對應的向量,然後利用second-account求出值,再將新值
//放入原來向量,直到沒有括號爲止。
for(int m=j;m<i;i--){
number.remove(j);
operator.remove(j);
}
j=-1;i=num.length;
}
}
}
}
flag=!flag;
}
return account_second(number,operator);
} catch (Exception e) {
return "error";
}
}
protected static String account_second(Vector<Float> number,Vector<Character> operator){
try {
int k=0;
while(operator.size()>k){
char ch=operator.get(k);
if(operator.get(k)=='*'){
number.setElementAt(number.get(k) * number.get(k+1), k);
}
else if(ch=='%'){
number.setElementAt(number.get(k)%number.get(k+1),k);
}
else if(ch=='/'){
number.setElementAt(number.get(k)/number.get(k+1),k);
}
else if(ch=='+'||ch=='-') {
k++;
continue;
}
number.remove(k+1);
operator.remove(k);
}
while(!operator.isEmpty()){
char ch=operator.firstElement();
if(ch=='+'){
number.setElementAt(number.firstElement()+number.get(1),0);
}else if(ch=='-'){
number.setElementAt(number.firstElement()-number.get(1),0);
}
number.remove(1);
operator.remove(0);
}
return number.firstElement().toString();
} catch(Exception e){
return "input error";
}
}
}