請看下方↓↓ 🤓
筆者用了四五天的時間完成了這個小Demo,可能有什么不完善或者解決方案Low的問題,歡迎大家在評論區反映,共同學習。
這個基於逆波蘭式的計算器,是筆者最近 在看GUI 時的一個小想法,初衷只是想嘗試下事件驅動編程,做個簡單的+、-、*、/的簡易版本,隨着學習的深入,慢慢的想將這個Demo做的相對完善,那么就涉及到了()的 運算優先級結合問題和對任意長度的輸入做出計算,第一個版本的時候通過設置運算數數組可以實現定長的數據運算,后來考慮到 這兩點,筆者進行了深入的Google😂,了解到 波蘭式和逆波蘭式,本文中我們使用 逆波蘭式。
那么什么是波蘭式和逆波蘭式呢,我們人類從小所接觸的例如3+2,5*4這樣的,運算符在操作數中間的稱為中綴表達式,波蘭式就是運算符在操作數前面的運算表達式,相反,逆波蘭式就是運算符在操作數后面的運算表達式。比如上面的3+2使用逆波蘭式表示為32+,5*4表示為54*;當然目前 還不知道為什么要用逆波蘭式來解決運算表達式的解析,隨着本文的深入,相信你可以 有所收獲。
逆波蘭式的優點:
1.當運算符在操作數后面時,使用一個運算棧,從左至右掃面表達式,如果是數字則入棧,如果是運算符則依次出棧兩個元素進行運算,具體的運算取決於當前判斷的運算符
2.使用逆波蘭式可以忽略 () 。
本文的重點在於,中綴表達式->后綴表達式(逆波蘭式),完成這個過程需要:
1.創建一個用於存放運算符的棧
2.存放原始表達式的 字符串Arraylist
3.存放逆波蘭式的 字符串Arraylist
4.筆者上一篇文章 所提到的 StringTokenizer的字符串分割
中綴轉后綴過程中,若是運算符,需要同棧頂運算符做優先級判斷:
1.若棧空 直接入棧
2.若為左括號 直接入棧
3.若是 右括號,以此出棧,並輸出到存儲逆波蘭式的arraylist中,直至當前棧頂為 ( , 將 ( 直接出棧,不存入arraylist。
4.若當前棧頂元素是 ( ,則直接入棧
5.否則,判斷優先級 若當前運算符> 棧頂元素 直接入棧
6.若低於棧頂元素優先級 將當前棧頂元素輸出至 存儲逆波蘭表達式的arraylist中,並將當前操作符 與 下一個棧頂繼續進行判斷。
得到了 逆波蘭式后,我們需要計算 逆波蘭表達式的Result,這個過程比較簡單,使用一個運算棧,從左到右掃描逆波蘭式,遇到數字Push,遇到運算符,依次POP 兩個操作數,按照運算符 進行運算,將運算結果重新入棧,照此往復,直至 運算棧中只剩下一個元素,就是最終的結果。
好了,話不多說,貼代碼。
1 package 逆波蘭式; 2 3 import java.util.ArrayList; 4 import java.util.Scanner; 5 import java.util.Stack; 6 import java.util.StringTokenizer; 7 8 public class NBL { 9 10 // 操作符棧 11 private Stack<String> czf_stack = new Stack<>(); // 存放 運算符的棧 12 private ArrayList<String> ysbds_list = new ArrayList<>(); //存放 原始表達式的 arraylist 13 private ArrayList<String> nblbds_list = new ArrayList<>(); // 存放轉換后的 逆波蘭式 14 private static final int One = 1; // 15 private static final int Two = 3; // 16 private static final int Three = 5; //規定優先級 Three 最高 17 18 // 定義一個運算棧 19 private static Stack<String> ys_stack = new Stack<>(); 20 21 // 初始化 使用StringTokenizer分割 字符串 22 public NBL(String bdString) { 23 // TODO Auto-generated constructor stub 24 StringTokenizer stringTokenizer = new StringTokenizer(bdString, "+-*/()",true); 25 while(stringTokenizer.hasMoreTokens()){ 26 ysbds_list.add(stringTokenizer.nextToken()); 27 //System.out.println(stringTokenizer.nextToken()); 28 } 29 } 30 31 32 // 判斷 是否是數字 33 public boolean isNum(String str){ 34 if(str.matches("[0-9]+")){ //這里使用正則表達式 驗證是否是數字 35 //System.out.println("Y"); 36 return true; 37 }else{ 38 //System.out.println("N"); 39 return false; 40 } 41 } 42 43 // 判斷 是否是操作符 44 public boolean isCzf(String str){ 45 if(str.matches("[\\+\\-\\*\\/\\(\\)]")){ 46 //System.out.println("Y"); 47 return true; 48 }else{ 49 //System.out.println("N"); 50 return false; 51 } 52 } 53 54 // 獲取 優先級 55 public int getYxj(String str){ 56 57 switch(str){ 58 case "(":return Three; 59 case "*": 60 case "/":return Two; 61 case "+": 62 case "-":return One; 63 case ")":return 0; 64 65 default : return -1; 66 67 } 68 69 } 70 71 // 判斷優先級 72 public boolean isYxj(String str1,String str2){ 73 return getYxj(str1) > getYxj(str2); 74 } 75 76 // ********* 當 當前操作元素為 操作符時********** 這里是 核心代碼, 用於操作符棧的判斷 77 public void stack_czf(String czf){ 78 79 //判斷當前棧內是否為空 80 if(czf_stack.isEmpty()){ 81 czf_stack.push(czf); 82 return; 83 } 84 85 //判斷是否為 ( 86 if("(".equals(czf)){ 87 czf_stack.push(czf); 88 return; 89 } 90 91 //判斷是否為 ) 92 if(")".equals(czf)){ 93 String string = ""; 94 while(!"(".equals(string = czf_stack.pop())){ 95 nblbds_list.add(string); 96 } 97 return; 98 } 99 100 //如果 當前棧頂元素是 ( 直接入棧 101 if("(".equals(czf_stack.peek())){ 102 czf_stack.push(czf); 103 return; 104 } 105 106 // 判斷 與 棧頂元素的優先級 , > 為true 107 if(isYxj(czf, czf_stack.peek())){ 108 czf_stack.push(czf); 109 return; 110 } 111 112 if(!isYxj(czf, czf_stack.peek())){ 113 nblbds_list.add(czf_stack.pop()); 114 stack_czf(czf); //這里調用函數 本身,並將本次的操作數傳參 115 } 116 117 } 118 119 // 中綴 —> 后綴 120 public void zz_hz(){ 121 122 // 遍歷原始表達式list 123 for(String str:ysbds_list){ 124 125 //System.out.println("-> " + str); 126 127 if(isNum(str)){ 128 nblbds_list.add(str); 129 }else if(isCzf(str)){ 130 //TODO 131 stack_czf(str); 132 }else{ 133 System.out.println("非法"); 134 } 135 136 } 137 138 // 遍歷完原始表達式后 將操作符棧內元素 全部添加至 逆波蘭表達式list 139 140 while(!czf_stack.isEmpty()){ 141 //System.out.println("即將 " + czf_stack.peek()); 142 nblbds_list.add(czf_stack.pop()); 143 } 144 145 } 146 147 // 具體計算方法 148 public int jsff(String s1,String s2,String s3){ 149 int a = Integer.parseInt(s2); 150 int b = Integer.parseInt(s1); 151 switch(s3){ 152 case "+":return a+b; 153 case "-":return a-b; 154 case "*":return a*b; 155 case "/":return a/b; 156 default : return 0; 157 } 158 } 159 160 // 計算 逆波蘭式 161 public int js_nbl(){ 162 for(String str:nblbds_list){ 163 if(isNum(str)){ 164 ys_stack.push(str); 165 }else{ 166 ys_stack.push(String.valueOf(jsff(ys_stack.pop(), ys_stack.pop(), str))); 167 } 168 } 169 return Integer.parseInt(ys_stack.pop()); //最后 棧中元素 即為結果 170 } 171 172 // public void nbls_bc(){ 173 // for(String string:nblbds_list){ 174 // nbls_cc += string; 175 // } 176 // } 177 178 179 public static void main(String[] args) { 180 181 Scanner keyboard = new Scanner(System.in); 182 System.out.println("請輸入"); 183 String input = keyboard.nextLine(); 184 NBL nbl = new NBL(input); 185 String nbls_cc = new String(); 186 int result = 0; 187 nbl.zz_hz(); 188 //nbl.nbls_bc(); 189 System.out.println("對應的逆波蘭式為 :" + nbls_cc); 190 System.out.println("結果是:"); 191 result = nbl.js_nbl(); 192 System.out.println(result); 193 } 194 195 196 }
上面的代碼,是從 傳入字符串,分割字符串存儲在 原始字符串的arraylist中,然后從中綴表達式轉換成逆波蘭式保存在 逆波蘭式arraylist中,最后使用運算棧計算表達式結果。
下面的內容,是給計算器寫了個GUI,畢竟筆者最初的目的只是學習下GUI )逃 ,這個沒什么要說的,只是在做監聽器的時候,稍微注意一點。
GUI代碼:
1 package 逆波蘭式; 2 3 import java.awt.BorderLayout; 4 import java.awt.CardLayout; 5 import java.awt.Container; 6 import java.awt.FlowLayout; 7 import java.awt.GridLayout; 8 import java.awt.event.WindowEvent; 9 import java.awt.event.WindowListener; 10 11 import javax.swing.JButton; 12 import javax.swing.JFrame; 13 import javax.swing.JLabel; 14 import javax.swing.JPanel; 15 16 public class jyjsq { 17 18 public static void main(String[] args) { 19 20 JFrame jFrame = new JFrame("計算器V1.0 By 昕越科技"); 21 Container container = jFrame.getContentPane(); 22 JPanel jp1 = new JPanel(); 23 JPanel jp2 = new JPanel(); 24 jp1.setLayout(new GridLayout(4, 4)); 25 jp2.setLayout(new FlowLayout()); 26 JLabel jLabel = new JLabel("0"); 27 container.add(jLabel, BorderLayout.NORTH); 28 29 JButton[] jButtons = new JButton[16]; 30 String[] jbutton_name = {"AC","(",")","+","7","8","9","-","4","5","6","*","1","2","3","/"}; 31 32 //創建監聽器實例 33 jsq_jtq jtq = new jsq_jtq(jLabel); 34 35 for(int i=0;i<jButtons.length;i++){ 36 jButtons[i] = new JButton(jbutton_name[i]); 37 jButtons[i].addActionListener(jtq); 38 jp1.add(jButtons[i]); 39 } 40 JButton juButton_0 = new JButton("0"); 41 juButton_0.addActionListener(jtq); 42 jp2.add(juButton_0); 43 JButton jButton_dh = new JButton("="); 44 jButton_dh.addActionListener(jtq); 45 jp2.add(jButton_dh); 46 container.add(jLabel,BorderLayout.NORTH); 47 container.add(jp1, BorderLayout.CENTER); 48 container.add(jp2,BorderLayout.SOUTH); 49 jFrame.setBounds(800, 170, 260, 360); 50 jFrame.setVisible(true); 51 jFrame.setResizable(false); 52 jFrame.addWindowListener(new WindowListener() { 53 54 @Override 55 public void windowOpened(WindowEvent e) { 56 // TODO Auto-generated method stub 57 58 } 59 60 @Override 61 public void windowIconified(WindowEvent e) { 62 // TODO Auto-generated method stub 63 64 } 65 66 @Override 67 public void windowDeiconified(WindowEvent e) { 68 // TODO Auto-generated method stub 69 70 } 71 72 @Override 73 public void windowDeactivated(WindowEvent e) { 74 // TODO Auto-generated method stub 75 76 } 77 78 @Override 79 public void windowClosing(WindowEvent e) { 80 // TODO Auto-generated method stub 81 System.out.println("計算器已關閉"); 82 System.exit(0); 83 } 84 85 @Override 86 public void windowClosed(WindowEvent e) { 87 // TODO Auto-generated method stub 88 89 } 90 91 @Override 92 public void windowActivated(WindowEvent e) { 93 // TODO Auto-generated method stub 94 95 } 96 }); 97 98 } 99 100 }
最后是,監聽器的代碼:
1 package 逆波蘭式; 2 3 import java.awt.event.ActionEvent; 4 import java.awt.event.ActionListener; 5 import java.awt.event.WindowListener; 6 7 import javax.swing.JLabel; 8 9 public class jsq_jtq implements ActionListener{ 10 11 private String bds_cc = ""; 12 private static JLabel JLabel_fuben; 13 14 public jsq_jtq(JLabel jLabel) { 15 // TODO Auto-generated constructor stub 16 JLabel_fuben = jLabel; 17 } 18 19 @Override 20 public void actionPerformed(ActionEvent e) { 21 // TODO Auto-generated method stub 22 String command = e.getActionCommand(); 23 if(command == "AC"){ 24 bds_cc = ""; 25 JLabel_fuben.setText("0"); 26 }else if(command == "="){ 27 //TODO 28 NBL nbl = new NBL(bds_cc); 29 int result = 0; 30 nbl.zz_hz(); 31 result = nbl.js_nbl(); 32 nbl = null; 33 System.out.println(result); 34 bds_cc = String.valueOf(result); 35 JLabel_fuben.setText(String.valueOf(result)); 36 System.out.println("-> -> :" + bds_cc); 37 }else{ 38 bds_cc += command; 39 System.out.println("-> :" + bds_cc); 40 JLabel_fuben.setText(bds_cc); 41 } 42 43 } 44 45 46 }
貼兩張運行圖吧:
在這里筆者必須要做個檢討,筆者的變量命名和類名以及代碼規范真的是太爛了,提醒大家 類名 最好采用駝峰寫法,首字母也要大寫,變量名最好不要用拼音🤣(筆者英文實在是太爛了)。
好了,關於JAVA的東西,筆者要稍微放一放,開始進軍Android了(雖然筆者JAVA仍然很水 (:逃 ),不過 學長給我的建議是 以需求為驅動力來學習,后期 用到什么 過來惡補什么,這樣會學以致用,最近確實有所體會。
所以,未來筆者會更新一些Android方面的筆記,還是那句話,希望大家可以多批評建議。 (:逃