基于逆波兰式的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方面的笔记,还是那句话,希望大家可以多批评建议。 (:逃

posted @ 2017-05-22 07:40  昕无旁骛  阅读(4633)  评论(3编辑  收藏  举报