工做中遇到一個小問題,就是要作一個相似excel那種的公式的東西,就是A0+A1*B0那樣的公式,而後得出結果。算法
首先,分析。緩存
這不是計算器,計算器要比這個簡易,計算器是所按即所得,即你點擊+-之類的按鈕時候,你的數字已經確認了,你所要作的只是轉換一下string和decimal而已。ide
好比1+2*(2+4)/4-1函數
若是再算上冪運算,我打不出來冪運算的符號,尷尬。spa
咱們能夠這麼寫,好比,遇到的第一個數字是1,那麼定義一個變量firnum=1 第一個符號是+,定義一個變量 mark=+,第二個數字是2,頂一個一個變量secnum=2,第二個符號是*,這時候進行判斷,由於*/比加減的運算級別高,要先算乘除,因此,這裏1和+要緩存起來,繼續往下走,而後計算(),得出()內的數字是6,這時候先運算2*6,而後遇到/,計算12/4,再日後走,遇到-,這時候+-的運算級別同樣,則開始運算以前的1和+,而後依次運算,最後得出結果。excel
怎麼說呢,咱們姑且認爲這是一個方法吧,姑且認爲,這麼辛苦了,寫了這麼多代碼,能進行四則運算,還挺正確,也不容易,沒有功勞也有苦勞。code
public decimal CalcRet(string str) { //第一個數字 string firStr = string.Empty; decimal firNum = 0m; //第二個數字; string secStr = string.Empty; decimal secNum = 0m; //temp數字 string tempStr = string.Empty; //當前計算符號 char curMark = '!'; //結果 decimal result = 0m; //上一個符號 char lastMark = '!'; for (int i = 0; i < str.Length; i++) { char c = str[i]; //判斷若是是數字和. if ((47 < c && c < 58) || c == '.') { //除卻第一次是第一個數字須要轉換,之後都是第一個和第二個進行累加 if (curMark == '!') { firStr += c; } else { if (curMark == '+' || curMark == '-') { secStr += c; } else if (curMark == '*' || curMark == '/') { if (lastMark == '+' || lastMark == '-') { tempStr += c; } else { secStr += c; } } } continue; } if (firStr != "") { decimal.TryParse(firStr, out firNum); firStr = ""; } if (c == '+' || c == '-' || c == '*' || c == '/') { switch (curMark) { case '+': if (secStr != "" && tempStr != "") { secNum = OperCalc(curMark, secNum, tempStr); firNum = firNum + secNum; secStr = ""; tempStr = ""; } if (c == '*' || c == '/') { lastMark = curMark; curMark = c; break; } if (secStr == "") continue; firNum = OperCalc(curMark, firNum, secStr); curMark = c; lastMark = c; secStr = ""; break; case '-': if (secStr != "" && tempStr != "") { secNum = OperCalc(curMark, secNum, tempStr); firNum = firNum - secNum; secStr = ""; tempStr = ""; } if (c == '*' || c == '/') { lastMark = curMark; curMark = c; break; } if (secStr == "") continue; firNum = OperCalc(curMark, firNum, secStr); curMark = c; lastMark = c; secStr = ""; break; case '*': if (lastMark != '!' && tempStr != "") { secNum = OperCalc(curMark, secStr, tempStr); secStr = secNum.ToString(); tempStr = ""; } else { firNum = OperCalc(curMark, firNum, secStr); secStr = ""; curMark = c; break; } if (c == '+' || c == '-') { if (lastMark != '!') { firNum = OperCalc(lastMark, firNum, secNum); secStr = ""; tempStr = ""; } } curMark = c; break; case '/': if (lastMark != '!' && tempStr != "") { secNum = OperCalc(curMark, secStr, tempStr); secStr = secNum.ToString(); tempStr = ""; } else { firNum = OperCalc(curMark, firNum, secStr); secStr = ""; curMark = c; break; } if (c == '+' || c == '-') { if (lastMark != '!') { firNum = OperCalc(lastMark, firNum, secNum); secStr = ""; tempStr = ""; } } curMark = c; break; case '(': break; case ')': break; default: curMark = c; if (c == '+' || c == '-') lastMark = c; break; } } else if (c == '(') { int temp = 1; for (int j = i + 1; j < str.Length; j++) { var k = str[j]; if (k == '(') { temp++; } else if (k == ')') { temp--; } if (temp == 0) { temp = j - i - 1; } } var kh = CalcRet(str.Substring(i + 1, temp)); if (lastMark != '!') { if (secStr != "") { tempStr = kh.ToString(); } else { secNum = kh; secStr = kh.ToString(); } } else { if (i == 0) { firNum = kh; } else { secNum = kh; secStr = kh.ToString(); } } i += temp + 1; } } if (tempStr != "") { secNum = OperCalc(curMark, secStr, tempStr); secStr = secNum.ToString(); result = OperCalc(lastMark, firNum, secStr); } else { result = OperCalc(curMark, firNum, secStr); } return result; } decimal OperCalc(char mark, string fir, string sec) { decimal a, b; decimal.TryParse(fir, out a); decimal.TryParse(sec, out b); switch (mark) { case '+': return a + b; case '-': return a - b; case '*': return a * b; case '/': return a / b; default: return 0m; } } decimal OperCalc(char mark, decimal fir, string sec) { decimal b; decimal.TryParse(sec, out b); switch (mark) { case '+': return fir + b; case '-': return fir - b; case '*': return fir * b; case '/': return fir / b; default: return 0m; } } decimal OperCalc(char mark, decimal fir, decimal sec) { switch (mark) { case '+': return fir + sec; case '-': return fir - sec; case '*': return fir * sec; case '/': return fir / sec; default: return 0m; } }
對,就是這種寫法。blog
在我看來,這麼寫的代碼,真的只是一堆垃圾,我不是針對誰,我是說寫成這樣的邏輯,它就是垃圾,連沒畢業的大學生都不如。遞歸
好比,若是加冪運算若是加mod怎麼辦,我就問你怎麼辦?ip
繼續判斷?
寫不死你!
而後,咱們能夠換個思路。
所謂運算,不過是兩個數字和一個符號之間故事,抱歉,我以爲一對一那種男女關係不適用於這裏,開個玩笑,呵呵!強行尬聊~
1+2 是1,2 兩個數字和+之間的運算
1+2+3*(4+5),是12345數字和四個符號進行的運算,至於括號,咱們是否是能夠把括號當成一個遞歸?就是4+5當成一個遞歸,調用同一個函數,返回一個結果就好了
也就是說,數字永遠比符號多一個
咱們是否是能夠這麼想。
list1 ={1,2,3,4,5}
list2={+,+,*,(+)}
第一次運算後
list1 ={1,2,3,9}
list2={+,+,*}
按照優先級,咱們可先計算*
獲得
list1 ={1,2,3,9}{1,2,27}
list2={+,+,*}{+,+}
刪掉*和最後的9,同時刪掉一個符號和一個數字,獲得
list1 ={1,2,27}
list2={+,+}
繼續
list1 ={3,27}
list2={+}
再繼續
list1 ={30}
list2={}
最後就剩下一個數字,好,計算完畢
public class CalcOperation { /// <summary> /// 計算字符串解析表達式 1+2(2*(3+4)) /// </summary> /// <param name="str">傳入的字符串</param> /// <returns>計算獲得的結果</returns> public decimal CalcStr(string str) { decimal num = 0m; //數字集合 List<decimal> numList = new List<decimal>(); //操做符集合 List<Operation> operList = new List<Operation>(); string strNum = ""; for (int i = 0; i < str.Length; i++) { char c = str[i]; //判斷若是是數字和. if ((47 < c && c < 58) || c == '.') { strNum += c; if (i == str.Length - 1) { if (!string.IsNullOrEmpty(strNum)) { decimal.TryParse(strNum, out num); numList.Add(num); strNum = ""; } } continue; } else if (c == '(') { int temp = 1; for (int j = i + 1; j < str.Length; j++) { var k = str[j]; if (k == '(') { temp++; } else if (k == ')') { temp--; } if (temp == 0) { temp = j - i - 1; } } strNum = str.Substring(i + 1, temp); numList.Add(CalcStr(strNum)); strNum = ""; i += temp + 1; } else { if (!string.IsNullOrEmpty(strNum)) { decimal.TryParse(strNum, out num); numList.Add(num); strNum = ""; } if (c == '+') { operList.Add(new AddOperation()); } else if (c == '-') { operList.Add(new SubOperation()); } else if (c == '*') { operList.Add(new MultipOperation()); } else if (c == '/') { operList.Add(new DivOperation()); } else if (c == '%') { operList.Add(new ModOperation()); } else { operList.Add(null); } } } List<int> tempOrder = new List<int>(); operList.ForEach(w => { if (!tempOrder.Contains(w.PrioRity)) { tempOrder.Add(w.PrioRity); } }); tempOrder.Sort(); for (int t = 0; t < tempOrder.Count; t++) { for (int i = 0; i < operList.Count; i++) { if (operList[i].PrioRity == tempOrder[t]) { numList[i] = operList[i].OperationResult(numList[i], numList[i + 1]); numList.RemoveAt(i + 1); operList.RemoveAt(i); i--; } } } if (numList.Count == 1) return numList[0]; return 0m; } public class Operation { protected int priority = 99; /// <summary> /// 優先級 /// </summary> public virtual int PrioRity { get { return priority; } set { priority = value; } } public virtual decimal OperationResult(decimal a, decimal b) { return 0m; } } public class AddOperation : Operation { public override decimal OperationResult(decimal a, decimal b) { return a + b; } } public class SubOperation : Operation { public override decimal OperationResult(decimal a, decimal b) { return a - b; } } public class MultipOperation : Operation { public override int PrioRity { get { return 98; } } public override decimal OperationResult(decimal a, decimal b) { return a * b; } } public class DivOperation : Operation { public override int PrioRity { get { return 98; } } public override decimal OperationResult(decimal a, decimal b) { return a / b; } } public class ModOperation : Operation { public override int PrioRity { get { return 97; } } public override decimal OperationResult(decimal a, decimal b) { return a % b; } } }
PrioRity這個是優先級,我比較懶,就從99往上了可是這樣真的很明瞭啊,並且能夠隨時添加新的算法,簡直了我想說的是,能簡便的儘可能簡便,能通運的儘可能通用,本身看的舒服,別人看的也舒服,是否是~