基於逆波蘭式的JAVA計算器


請看下方↓↓  🤓

  筆者用了四五天的時間完成了這個小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方面的筆記,還是那句話,希望大家可以多批評建議。 (:逃


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM