摘要:對于理論算法不再這累贅了。在查閱資料的時候發現算法不管用棧還是正則等等,似乎只處理操作符是的數,這是很不可取的。所以需要先將中綴表達式轉化成后綴并標記多位數的操作符,然后在處理后綴表達式。
最后一次更新于2019/07/08
效果演示圖 功能與流程要制作一個簡易計算器,首先你要清楚GUI里要顯示什么:
結果顯示框
0~9的數字
刪除功能
清楚功能
搜尋歷史記錄功能
計算結果的功能
括號優先計算功能
接下來通過流程圖簡單介紹一下思路:
以下代碼是根據我的設計來編寫的
/** * @author Hephaest * @since 2018/04/19 * JDK 1.6 */ import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.UIManager; /** * Calculator類用來創造GUI */ public class Calculator extends JFrame { //新建文本框 JTextField text = new JTextField(); // set up row 2 JPanel row2 = new JPanel(); //創建按鈕們 String[][] buttons = {{"7","8","9","DEL","AC"},{"4","5","6","×","÷"},{"1","2","3","+","-"},{"0","(",")","Replay","="}}; JButton[][]button = new JButton[4][5]; /** * 這個計算機的界面我模擬的是卡西歐fx-82ES PLUS A * 但是僅有其中的部分功能 */ public Calculator() { super("CASIO"); setSize(400,300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); //設置文本框的尺寸、位置以及禁止鍵盤輸入 text.setPreferredSize(new Dimension(30, 40)); text.setHorizontalAlignment(SwingConstants.TRAILING); text.setEditable(false); getContentPane().add(text, BorderLayout.NORTH); //聲明每一個按鈕代表的意義 add(row2, BorderLayout.CENTER); GridLayout layout2 = new GridLayout(4,5,5,5); row2.setLayout(layout2); for(int i = 0; i < buttons.length; i++) { for(int j = 0; j < buttons[0].length; j++) { button[i][j] = new JButton(buttons[i][j]); row2.add(button[i][j]); } } add(row2); setResizable(false); setVisible(true); } private static void setLookAndFeel() { //這條使跨操作系統也能看到計算機的GUI try { UIManager.setLookAndFeel( "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel" ); } catch (Exception exc) { // ignore error } } public static void main(String[] args) { Calculator.setLookAndFeel(); Calculator cl = new Calculator(); cl.listener(); } /** * 事件監聽器,一旦按下按鈕就要根據操作歷史進行相應的反應 */ public void listener() { Listener l = new Listener(this); for(int i = 0; i < buttons.length; i++) { for(int j = 0; j < buttons[0].length; j++) { button[i][j].addActionListener(l); } } } }事件監聽器源碼
有了按鈕后下一步就是要想辦法實現按鈕功能,我的思路在上面流程圖里給過了,不再累贅,直接看如何利用代碼實現:
/** * @author Hephaest * @since 2018/04/19 * JDK 1.6 */ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JButton; import javax.swing.JTextField; /** * Listener 類 * 用來將按鈕輸入的結果通過鏈表的方式一個字一個字存儲在字符串里,然后調用另一類計算整個字符串,返回一個值 */ public class Listener implements ActionListener { private Calculator cl; private ArrayListlist=new ArrayList (); private ArrayList his=new ArrayList ();//這個鏈表用來添加每一次得到的最終的結果 private ArrayList arr = new ArrayList ();//把his里的一整串字符分割成單個字符,再連接 private String[] arrayStr = new String[] {};//儲存單次的歷史記錄 private String out = ""; private String output = ""; public Listener(Calculator cl) { this.cl = cl; } public void actionPerformed(ActionEvent event) { JButton button = (JButton) event.getSource(); /** * 如果點“=”,計算整個表達式的結果,如果是錯誤表達式,在文本框輸入“Input Error!” */ if(button.getText().equals("=")) { try { Function f = new Function(); double result = f.compute(out); cl.text.setText(Double.toString(result)); } catch(Exception e) { cl.text.setText("Input Error!"); } } else if(button.getText().equals("×")) { /** * 如果點擊"×",先把它轉換為"*" */ if(list.isEmpty()) { arr.add("*"); output += "*"; out = output; cl.text.setText(output); } else { list.add("*"); output += "*"; out = output; cl.text.setText(output); } } else if(button.getText().equals("÷")) { /** * 如果點擊"÷",把它轉換為"/" */ if(list.isEmpty()) { arr.add("/"); output += "/"; out = output; cl.text.setText(output); } else { list.add("/"); output += "/"; out = output; cl.text.setText(output); } } else if(button.getText().equals("DEL")) { /** * 如果點擊"DEL",刪除表達式里最后一個字符,每點一次刪一個 */ if(list.isEmpty()) { arr.remove(arr.size()-1); output = ""; for(int i = 0; i < arr.size(); i++) output += arr.get(i); out = output; cl.text.setText(output); } else { list.remove(list.size()-1); String output = ""; for(int i = 0; i < list.size(); i++) output+=list.get(i); out = output; cl.text.setText(output); } } else if(button.getText().equals("AC")) { /** * 如果點擊"AC",刪除list鏈表,再刪除之前先把表達式保留到his的鏈表里 */ his.add(out); list.clear(); output=""; cl.text.setText(output); } else if(button.getText().equals("Replay")) { /** * 如果點擊"Replay",在文本框里顯示上一條表達式 */ output=his.get(his.size()-1); cl.text.setText(output); arr.clear(); //把上一條表達式分割成單個字符的字符數組 char[] a=output.toCharArray(); for(int i=0;i 計算器表達式算法 關于如何分析整個表達式并計算出正確值,實踐了一下,還是后綴表達式比較方便,不用考慮括號這種優先級問題。對于理論算法不再這累贅了。在查閱資料的時候發現算法不管用棧還是正則等等,似乎只處理操作符是0~9的數,這是很不可取的。什么意思呢?就是像10/5這樣的算數是沒辦法解決的,只能解決像5/5這樣的。所以需要先將中綴表達式轉化成后綴并標記多位數的操作符,然后在處理后綴表達式。
/** * @author Hephaest * @since 2018/07/13 * JDK 1.6 */ import java.util.LinkedList; import java.util.List; import java.util.Stack; public class function { private String[] str=new String[10]; private int begin; public function(){} /** * 中綴表達式轉換成后綴表達式 * @param exp 在計算器上顯示的文本 中綴表達式 * @return 正確的計算結果 */ public double compute(String exp) { char[] ch = exp.toCharArray(); Stack源碼地址stack = new Stack<>(); String convertToPostfix = new String(); int size = ch.length; begin = 0; for (int i = 0; i < size; i++) { //遇到左括號直接入棧 if(ch[i] == "(") stack.push(ch[i]); else if(ch[i] == ")") { //遇到右括號出棧(追加到后綴表達式), 直到出棧的元素為左括號或為0 char popValue = stack.pop(); do { convertToPostfix = convertToPostfix.concat(String.valueOf(popValue)); popValue = stack.pop(); }while(!stack.isEmpty() && popValue != "("); } else if(checkOperator(ch[i])) { /* * 遇到運算符需要判斷: * 1.是否為空棧,是的話直接入棧 * 2.即將入棧的運算符是否比棧頂元素優先級高 * 是,直接入棧 * 否,棧頂元素出棧(追加到后綴表達式),當前運算符入棧 */ if(stack.isEmpty()) stack.push(ch[i]); else { char popValue = stack.pop(); while(checkPriority(popValue,ch[i])) { convertToPostfix = convertToPostfix.concat(String.valueOf(popValue)); if(stack.isEmpty()) break; popValue = stack.pop(); } if(!checkPriority(popValue,ch[i])) stack.push(popValue); stack.push(ch[i]); } } else if(checkDigital(ch[i])) { /* * 單個數字直接追加到后綴表達式 * 含有不止一個數字的操作符需要做記錄: * 1.計算該操作符的起始位置和終止位置 * 2.把數字傳到字符串數組里(全局變量,下一步需要用到) */ if(i + 1 < size && i - 1 >= 0) { if(checkDigital(ch[i - 1]) && !checkDigital(ch[i + 1])) { int end = i; int j = end; while(checkDigital(ch[j])) { j--; } j++; List elements = new LinkedList<>(); do { elements.add(String.valueOf(ch[j])); j++; } while(j <= end); str[begin] = String.join("", elements); System.out.println(str[begin]); begin++; } } convertToPostfix=convertToPostfix.concat(String.valueOf(ch[i])); } } //第一遍結束后把棧中剩下的操作符依次出棧(追加到后綴表達式) while(!stack.isEmpty()) { char popValue = stack.pop(); convertToPostfix = convertToPostfix.concat(String.valueOf(popValue)); } System.out.println(convertToPostfix); return computeResult(convertToPostfix); } /** * 計算后綴表達式 * @param convertToPostfix 后綴表達式的字符串 * @return 計算結果 */ public double computeResult(String convertToPostfix) { int[] index=new int[10]; /* * 判斷是否有多位數的操作符,有的話找到在后綴表達式的初始位置 * 如果沒有的話就不會執行 */ for(int i = 0;i < begin; i++) { index[i] = convertToPostfix.indexOf(str[i]); System.out.println(index[i]); } char[] ch = convertToPostfix.toCharArray(); Stack stack = new Stack<>(); double result = 0; for (int i = 0; i < ch.length; i++) { //如果是運算符,pop出棧頂的兩個元素,記住先進后出 if(checkOperator(ch[i])) { double num2=stack.pop(); System.out.println("num2" + num2); System.out.print(" "); double num1=stack.pop(); System.out.println("num1" + num1); System.out.print(" "); switch(ch[i]) { case "*": result = num2 * num1; break; case "/": result = num1 / num2; break; case "+": result = num1 + num2; break; case "-": result = num1 - num2; break; } System.out.println(result); stack.push(result); } else { /* * 對于多位操作符,需要把單個字符連接起來然后作為一個雙精度數放入棧中 * 一位數的操作符直接放入棧即可,注意從字符變成數字時要減去48(0的字符型數據) */ int stop = 0; for(int j = 0; j < begin; j++) { if(i == index[j]) { int start=i; List elements = new LinkedList<>(); do { elements.add(String.valueOf(ch[i])); i++; } while(i < str[j].length() + start); i--; String test=String.join("", elements); stack.push(Double.valueOf(test)); stop=1; break; } } if(stop == 0) stack.push((double)ch[i]-48); } } System.out.print(" "); System.out.print(result); return result; } /** * 判斷是否是運算符 * @param c 當前字符 * @return 布爾型結果 */ public boolean checkOperator(char c) { int result; switch(c) { case "+": case "-": case "*": case "/": result = 1; break; default: result = 0; } if(result == 1) return true; else return false; } /** * 判斷是否是數字 * @param c 當前字符 * @return 布爾型結果 */ public boolean checkDigital(char c) { int num = c; num -= 48; if(num >= 0 && num <= 9) return true; else return false; } /** * 判斷即將入棧的優先級是否更高 * @param popOne 棧頂元素 * @param checkOne 即將入棧元素 * @return 布爾型結果 */ public boolean checkPriority(char popOne,char checkOne) { if((popOne == "*" || popOne == "/") && (checkOne == "+" || checkOne == "-")) return true; else if(popOne == checkOne) return true; else return false; } } 如果我的文章可以幫到您,勞煩您點進源碼點個 ★ Star 哦!
https://github.com/Hephaest/S...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69137.html
摘要:代碼實現在控制臺打印總結本篇文章帶大家搭好環境,并體驗了控制臺打印。輸出結果總結熟練掌握取余和整除運算,大有作用。終止本次循環,繼續執行下一次循環。 ?本文收錄...
摘要:因為管理人員是了解手下的人員以及自己負責的事情的。處理器優化和指令重排上面提到在在和主存之間增加緩存,在多線程場景下會存在緩存一致性問題。有沒有發現,緩存一致性問題其實就是可見性問題。 網上有很多關于Java內存模型的文章,在《深入理解Java虛擬機》和《Java并發編程的藝術》等書中也都有關于這個知識點的介紹。但是,很多人讀完之后還是搞不清楚,甚至有的人說自己更懵了。本文,就來整體的...
摘要:因為管理人員是了解手下的人員以及自己負責的事情的。處理器優化和指令重排上面提到在在和主存之間增加緩存,在多線程場景下會存在緩存一致性問題。有沒有發現,緩存一致性問題其實就是可見性問題。 網上有很多關于Java內存模型的文章,在《深入理解Java虛擬機》和《Java并發編程的藝術》等書中也都有關于這個知識點的介紹。但是,很多人讀完之后還是搞不清楚,甚至有的人說自己更懵了。本文,就來整體的...
摘要:在領域,實現并發程序的主要手段就是多線程。可運行狀態指的是線程可以分配執行。當等待的事件出現了,線程就會從休眠狀態轉換到可運行狀態。導出線程棧,分析線程狀態是診斷并發問題的一個重要工具。 在 Java 領域,實現并發程序的主要手段就是多線程。線程是操作系統里的一個概念,雖然各種不同的開發語言如 Java、C# 等都對其進行了封裝,但原理和思路都是相同都。Java 語言里的線程本質上就是...
閱讀 2229·2019-08-30 10:51
閱讀 785·2019-08-30 10:50
閱讀 1463·2019-08-30 10:49
閱讀 3130·2019-08-26 13:55
閱讀 1591·2019-08-26 11:39
閱讀 3412·2019-08-26 11:34
閱讀 1937·2019-08-23 18:30
閱讀 3381·2019-08-23 18:22