因爲工做須要最近在研究PHP擴展,無可避免的涉及到了C語言。從出了學校之後C語言在實際工做中尚未用到過,因此必需要先進行一點複習工做。我的認爲對於熟悉同樣東西說最好的方法是上手實踐。因而便想起了當時大學的時候老師佈置過的一道題目,用C語言實現簡單數學表達式的分析和求值,比較遺憾的是當初沒能把題目完成。就想着重新試一試,算是補一下當初的做業。正則表達式
還記得當初的思路是,循環C字符串。用鏈表將不一樣的計算項存儲到鏈表中。而後在進行循環求值。若是遇到括號就遞歸調用。回憶並整理了一下當初的思路大體以下。微信
1.輸入 3+5*(2-6)/2函數
2.解析爲 測試
3.計算 經過兩次循環對不一樣優先級進行運算得出結果spa
第一次對*/進行計算,拿當前節點,和節點的next節點,求值後將值賦給next節點,並刪除自身節點指針
第二次對+-進行求值,直到遇到end對象
最初也打算按這個思路去實現的,以後發現解析的步驟會比較複雜,涉及到屢次的字符串搜索,比對。再加上C自己不支持正則表達式,因此就放棄了當初的思路。找了一些相關的資料後發現了一種更簡單也更科學的方法,那就是將輸入轉換成後綴表達式再進行求值,這後綴表達式到底是什麼呢,請繼續看下去。blog
1、後綴表達式遞歸
百度百科介紹: 不包含括號,運算符放在兩個運算對象的後面,全部的計算按運算符出現的順序,嚴格從左向右進行(再也不考慮運算符的優先規則,如:(2 + 1) * 3 , 即2 1 + 3 *字符串
瞭解了後綴表達式才知道,原來咱們習覺得常的數學表達式被稱之爲中綴表達式。花了點時間研究了一下發現後綴表達式的計算還蠻簡單的,也更符合計算機運算。
計算方法,從左往右進行計算。取運算符號前兩位數字進行運算,運算結果替代運算符以及前兩位數字,持續運算到最右邊得出結果。
這麼提及來可能比較難理解,咱們看幾個例子
最簡單的 21+
計算過程爲 2 + 1 = 3
值爲 3
普通的 325-2*+
計算過程 從左往右先計算 2-5=-3,-3取代前面的25-以後爲 3-32*+,完整的計算步驟以下
計算2-5=3 計算完以後表達式爲 3 -3 2 *+
計算-3*2=-6 計算完以後表達式爲 3 -6 +
計算3+-6=-3 計算完以後表達式爲 -3
值爲 -3
稍微複雜一點的 21+3*5387-/*-
計算過程 從左往右先計算 2+1 = 3,4取代前面的21+以後爲 33*5387-/*- 完整的計算步驟以下
計算2+1=3 計算完以後表達式爲 3 3 *5 3 8 7 -/*-
計算3*3=9 計算完以後表達式爲 9 5 3 8 7 -/*-
計算8-7=1 計算完以後表達式爲 9 5 3 1 /*-
計算3/1=3 計算完以後表達式爲 9 5 3 *-
計算5*3=15 計算完以後表達式爲 9 15 -
計算9-15=-6 結果爲 -6
值爲 -6
看完了以上結果,咱們會發現每一次參與計算的數字,都是最靠近運算符號的兩位數字。而後由運算出來的結果代替參與運算的數字和運算符,直到表達式只剩下一個值,計算完成。
根據這樣的規律,程序處理起來就簡單了。
1.從左往右的循環整個輸入。
2.判斷是不是數字,若是是數字就保存起來。若是遇到符號,則把保存的前兩個值取出來,計算後把本次計算結果存回去
3.循環完成以後,剩下的表達式即是計算結果了
根據如上規則不難發現每次參與計算的兩個數字都是最後存進去的,這樣一來咱們即可以用棧輕鬆的完成這樣一個程序了,下面跟你們簡單介紹一下棧。
2、棧
百度百科的解釋比較複雜,就不摘抄了。其實棧能夠簡單的理解爲一個存放數據的空間,數據按照後進先出的原則進行存取。對數據的操做有push和pop,分別稱之爲壓入,彈出。
因爲比較簡單,因此直接用代碼實現了一個簡單的棧,包含以下四個方法。
簡單的進行測試,輸入結果爲
item is : 1.120000
item is : 2.800000
2.800000
1.120000
實現了預期輸出,一個簡單的棧就搞定了,接下來就能夠利用整個簡單的棧來完成求值的函數了。
3、後綴表達式求的具體實現
因爲棧已經實現了,因此只須要按照後綴表達式求值的邏輯進行運算在配合棧就能夠實現整個計算過程了。方法比較簡單,用while循環整字符串,在配合switch對數字和運算符作不一樣的處理就可以完成一個簡單的後綴表達式求值函數了。如下是初版的實現代碼
在第一個版本的過程當中,遇到一個C語言知識點是 C語言的字符串指針指向的地址是字符串第一個字符的地址。
因此當i爲0的時候,&str[i] = &str, atof 接收的是一個字符串指針,若是使用 STACKpush(atof(&str[i])),i爲0時,會將整個字符串傳入進去轉換。採用了一個char變量,講str[i]拷貝出來,而後傳入&num,則能夠解決這個問題。
其實這段程序裏還涉及到一個指針運算的知識點,可是這裏的程序裏涉及還比較簡單易懂,後續還有更難的地方涉及到這個知識點,因此先放到後面再跟你們分享。
對上面的方法進行了測試,輸入咱們以前分析的三個表達式,得出結果以下
printf("%f\n", calculate("21+")); //3
printf("%f\n", calculate("325-2*+")); //-3
printf("%f\n", calculate("21+3*5387-/*-")); //-6
測試經過,跟以前的計算結果一直。以上即是一個簡單的後綴表達式的計算程序了。進行了多幾回的測試發現了一個小問題,就是目前沒法進行多位數的識別。由於程序沒一次都將一位數壓入站內了。思考了一下在表達式的每一個計算項上加了一個空格符做爲數字的區分。變爲 21 3 +這種形式,因而動手將代碼作了一點小改動
在 switch 中加入了對空格的處理,以及多位數的處理
//若是遇到空格,則重置標誌位
這樣一來就能夠識別多位數了。經測試
printf("%f\n", calculate("21 1+")); //22
printf("%f\n", calculate("2 11+")); //13
獲得了正確的結果,整個簡單計算器的第一步計算後綴表達式完成。
有興趣的能夠測試一下上的代碼,若是遇到什麼問題能夠經過微信公衆號反饋給我。
固然這個程序還有一些有待完善的地方。如表達式合法性檢查,對小數的處理,以及對負數的處理等,暫時先預留着後續再跟你們分享如何實現以及會用到的知識點。
下一篇將爲你們介紹簡單數學表達式計算的第二步,如何實現將中綴表達式轉換爲後綴表達式。
歡迎你們關注微信公衆號~