回覆列表
-
1 # SunnyZhang的IT世界
-
2 # 帖木兒
編譯原理課第一章就該講的東西。
一個運算元棧,一個運算子棧。遇到運算元壓(數)棧,遇到左括號壓(符)棧,遇到右括號一路彈棧至左括號,遇到運算子和(符)棧頂比優先順序,新的更高則壓棧,更低或相同則一路彈棧至更高優先順序算符或空。
編譯原理是CS基礎課,表示式分析又是基礎課裡的入門內容。一般到大三總該學到了。
這個是有成熟的演算法的。不過需要將日常的四則運算表示式轉換為另外一種表示形式,稱為逆波蘭表示式。下面我們具體介紹一下。
我們日常的運算表示式通常是如下形式,這種成為中綴表示式,也就是運算子在運算數的中間。這種表示式人類人容易識別,並根據其進行計算,但計算機識別這種表示式非常困難。
a + b * (c - d) + e/f
因此,1920年,波蘭科學家揚·武卡謝維奇(Jan ukasiewicz)發明了一種不需要括號的計算表示式的表示法將運算子號寫在運算元之前,也就是字首表示式,即波蘭式(Polish Notation, PN)。上述中綴表示式轉換為波蘭表示式的格式如下:
+a+*b-cd/ef
從上面表示式可以看出,運算子在2個運算數的前面,因此波蘭表示式也稱為字首表示式。為了便於理解,我們給出一個具體的例項,這個例項將上面的字母換成具體的數字(1 + 2 * (4 - 3) + 6/2),這個結果很容易看出來,也就是1 + 2*1 + 3 = 6。然後我們看一下波蘭表示式的表示形式及運算過程。
+1+*2-4 3/ 6 2 // 從右向左掃描,當遇到運算子時計算其最近的右側2個運算數+1+*2-4 3 3 //先計算最右側的資料,也就是 6/2=3+1+*2 1 3 // 同理,4-3 = 1+1+2 3 // 同理, 2*1= 1+1+5 6
透過上面示例可以大概理解波蘭表示式的計算過程,上面的運算子和運算數就可以透過通用的資料結構進行計算(請自己思考一下,我們後面再介紹)。
什麼是逆波蘭表示式前面瞭解了波蘭表示式,那什麼是逆波蘭表示式呢?波蘭表示式也成為字首表示式,而逆波蘭表示式則成為字尾表示式,對比可以猜出來運算子在運算數後面的表示式就是逆波蘭表示式。上述表示式如果採用逆波蘭表示式則如下:
1 2 4 3 -*+ 6 2 / + //計算方式正好相反,也就是從左向右掃描1 2 1 *+ 6 2 / +1 2 + 6 2 / +3 6 2 / +3 3 +6
從中綴表示式轉換為逆波蘭(字尾)表示式從中綴表示式轉換為字尾表示式的流程如下描述。需要注意的是,經過處理之後,中綴表示式中的括弧將被消除,也就是隻剩下+-*/數學運算子。
從左至右掃描一中綴表示式。若讀取的是運算元,則判斷該運算元的型別,並將該運算元存入運算元堆疊若讀取的是運算子該運算子為左括號"(",則直接存入運算子堆疊。該運算子為右括號")",則輸出運算子堆疊中的運算子到運算元堆疊,直到遇到左括號為止。該運算子為非括號運算子:- 若運算子堆疊棧頂的運算子為括號,則直接存入運算子堆疊。- 若比運算子堆疊棧頂的運算子優先順序高,則直接存入運算子堆疊。- 若比運算子堆疊棧頂的運算子優先順序低或者相等,則輸出棧頂運算子到運算元堆疊,並將當前運算子壓入運算子堆疊。4. 當表示式讀取完成後運算子堆疊中尚有運算子時,則依序取出運算子到運算元堆疊,直到運算子堆疊為空。
將中綴表示式轉換成逆波蘭表示式過程中,特別要注意對於中綴標到式中括號的處理。
要注意的,如果算符是"(",無論入棧中棧頂級別(只看棧頂)為何直接入棧,所以,“(”的等級只用於對其後入棧的算符進行優先順序比較,在“(”入棧時是無視優先順序的。在遇到")"時候找到最後進入的"(",並把"("前面所有的符號都壓入出棧。不能僅憑運算子的級別來判斷。注: 逆波蘭用的優先順序有以下幾種, 等級從高到低分別是:
1.* \
2.+ -
3.(
4.)
上面關於流程的表示文字比較多,看起來可能比較頭疼。為了便於理解上述流程,我們依然透過上面的例項(1 + 2 * (4 - 3) + 6/2)進行介紹。在實際操作過程中需要一個棧和一個佇列,分別儲存運算子和運算元。
透過上面整個過程的描述,並結合演算法,相信大家對中綴表示式轉換為字尾表示式(波蘭表示式)已經非常清楚了。
逆波蘭表示式的計算逆波蘭表示式的計算就比較簡單了。以上面結果中的佇列為輸入,同時再準備一個棧用於運算。具體流程如下:
將佇列中的資料依次出隊,然後壓棧;在出隊的過程中如果遇到運算子,則從棧中彈出2個運算數,分別作為右運算數和左運算數,進行運算;將步驟2的運算結果入棧;跳入步驟1,繼續進行出隊操作。依然以上述內容為例進行介紹。如圖1中第一行左側為形成的佇列,右側是一個空棧。將佇列中運算元依次出隊,入棧。
在出隊的過程中遇到運算子(-),此時將操作數出棧進行運算(注意這裡運算元的順序)。
重複上述操作,直到將佇列中所有內容出隊。
綜上,我們可以透過將我們常見表示式字串轉換為逆波蘭表示式,然後就可以透過計算機程式計算了。