摘要:對(duì)于理論算法不再這累贅了。在查閱資料的時(shí)候發(fā)現(xiàn)算法不管用棧還是正則等等,似乎只處理操作符是的數(shù),這是很不可取的。所以需要先將中綴表達(dá)式轉(zhuǎn)化成后綴并標(biāo)記多位數(shù)的操作符,然后在處理后綴表達(dá)式。
最后一次更新于2019/07/08
效果演示圖 功能與流程要制作一個(gè)簡(jiǎn)易計(jì)算器,首先你要清楚GUI里要顯示什么:
結(jié)果顯示框
0~9的數(shù)字
刪除功能
清楚功能
搜尋歷史記錄功能
計(jì)算結(jié)果的功能
括號(hào)優(yōu)先計(jì)算功能
接下來(lái)通過(guò)流程圖簡(jiǎn)單介紹一下思路:
以下代碼是根據(jù)我的設(shè)計(jì)來(lái)編寫(xiě)的
/** * @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類(lèi)用來(lái)創(chuàng)造GUI */ public class Calculator extends JFrame { //新建文本框 JTextField text = new JTextField(); // set up row 2 JPanel row2 = new JPanel(); //創(chuàng)建按鈕們 String[][] buttons = {{"7","8","9","DEL","AC"},{"4","5","6","×","÷"},{"1","2","3","+","-"},{"0","(",")","Replay","="}}; JButton[][]button = new JButton[4][5]; /** * 這個(gè)計(jì)算機(jī)的界面我模擬的是卡西歐fx-82ES PLUS A * 但是僅有其中的部分功能 */ public Calculator() { super("CASIO"); setSize(400,300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); //設(shè)置文本框的尺寸、位置以及禁止鍵盤(pán)輸入 text.setPreferredSize(new Dimension(30, 40)); text.setHorizontalAlignment(SwingConstants.TRAILING); text.setEditable(false); getContentPane().add(text, BorderLayout.NORTH); //聲明每一個(gè)按鈕代表的意義 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() { //這條使跨操作系統(tǒng)也能看到計(jì)算機(jī)的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(); } /** * 事件監(jiān)聽(tīng)器,一旦按下按鈕就要根據(jù)操作歷史進(jìn)行相應(yīng)的反應(yīng) */ 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); } } } }事件監(jiān)聽(tīng)器源碼
有了按鈕后下一步就是要想辦法實(shí)現(xiàn)按鈕功能,我的思路在上面流程圖里給過(guò)了,不再累贅,直接看如何利用代碼實(shí)現(xiàn):
/** * @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 類(lèi) * 用來(lái)將按鈕輸入的結(jié)果通過(guò)鏈表的方式一個(gè)字一個(gè)字存儲(chǔ)在字符串里,然后調(diào)用另一類(lèi)計(jì)算整個(gè)字符串,返回一個(gè)值 */ public class Listener implements ActionListener { private Calculator cl; private ArrayListlist=new ArrayList (); private ArrayList his=new ArrayList ();//這個(gè)鏈表用來(lái)添加每一次得到的最終的結(jié)果 private ArrayList arr = new ArrayList ();//把his里的一整串字符分割成單個(gè)字符,再連接 private String[] arrayStr = new String[] {};//儲(chǔ)存單次的歷史記錄 private String out = ""; private String output = ""; public Listener(Calculator cl) { this.cl = cl; } public void actionPerformed(ActionEvent event) { JButton button = (JButton) event.getSource(); /** * 如果點(diǎn)“=”,計(jì)算整個(gè)表達(dá)式的結(jié)果,如果是錯(cuò)誤表達(dá)式,在文本框輸入“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("×")) { /** * 如果點(diǎn)擊"×",先把它轉(zhuǎn)換為"*" */ 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("÷")) { /** * 如果點(diǎn)擊"÷",把它轉(zhuǎn)換為"/" */ 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")) { /** * 如果點(diǎn)擊"DEL",刪除表達(dá)式里最后一個(gè)字符,每點(diǎn)一次刪一個(gè) */ 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")) { /** * 如果點(diǎn)擊"AC",刪除list鏈表,再刪除之前先把表達(dá)式保留到his的鏈表里 */ his.add(out); list.clear(); output=""; cl.text.setText(output); } else if(button.getText().equals("Replay")) { /** * 如果點(diǎn)擊"Replay",在文本框里顯示上一條表達(dá)式 */ output=his.get(his.size()-1); cl.text.setText(output); arr.clear(); //把上一條表達(dá)式分割成單個(gè)字符的字符數(shù)組 char[] a=output.toCharArray(); for(int i=0;i 計(jì)算器表達(dá)式算法 關(guān)于如何分析整個(gè)表達(dá)式并計(jì)算出正確值,實(shí)踐了一下,還是后綴表達(dá)式比較方便,不用考慮括號(hào)這種優(yōu)先級(jí)問(wèn)題。對(duì)于理論算法不再這累贅了。在查閱資料的時(shí)候發(fā)現(xiàn)算法不管用棧還是正則等等,似乎只處理操作符是0~9的數(shù),這是很不可取的。什么意思呢?就是像10/5這樣的算數(shù)是沒(méi)辦法解決的,只能解決像5/5這樣的。所以需要先將中綴表達(dá)式轉(zhuǎn)化成后綴并標(biāo)記多位數(shù)的操作符,然后在處理后綴表達(dá)式。
/** * @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(){} /** * 中綴表達(dá)式轉(zhuǎn)換成后綴表達(dá)式 * @param exp 在計(jì)算器上顯示的文本 中綴表達(dá)式 * @return 正確的計(jì)算結(jié)果 */ 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++) { //遇到左括號(hào)直接入棧 if(ch[i] == "(") stack.push(ch[i]); else if(ch[i] == ")") { //遇到右括號(hào)出棧(追加到后綴表達(dá)式), 直到出棧的元素為左括號(hào)或?yàn)? char popValue = stack.pop(); do { convertToPostfix = convertToPostfix.concat(String.valueOf(popValue)); popValue = stack.pop(); }while(!stack.isEmpty() && popValue != "("); } else if(checkOperator(ch[i])) { /* * 遇到運(yùn)算符需要判斷: * 1.是否為空棧,是的話(huà)直接入棧 * 2.即將入棧的運(yùn)算符是否比棧頂元素優(yōu)先級(jí)高 * 是,直接入棧 * 否,棧頂元素出棧(追加到后綴表達(dá)式),當(dāng)前運(yùn)算符入棧 */ 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])) { /* * 單個(gè)數(shù)字直接追加到后綴表達(dá)式 * 含有不止一個(gè)數(shù)字的操作符需要做記錄: * 1.計(jì)算該操作符的起始位置和終止位置 * 2.把數(shù)字傳到字符串?dāng)?shù)組里(全局變量,下一步需要用到) */ 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])); } } //第一遍結(jié)束后把棧中剩下的操作符依次出棧(追加到后綴表達(dá)式) while(!stack.isEmpty()) { char popValue = stack.pop(); convertToPostfix = convertToPostfix.concat(String.valueOf(popValue)); } System.out.println(convertToPostfix); return computeResult(convertToPostfix); } /** * 計(jì)算后綴表達(dá)式 * @param convertToPostfix 后綴表達(dá)式的字符串 * @return 計(jì)算結(jié)果 */ public double computeResult(String convertToPostfix) { int[] index=new int[10]; /* * 判斷是否有多位數(shù)的操作符,有的話(huà)找到在后綴表達(dá)式的初始位置 * 如果沒(méi)有的話(huà)就不會(huì)執(zhí)行 */ 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++) { //如果是運(yùn)算符,pop出棧頂?shù)膬蓚€(gè)元素,記住先進(jìn)后出 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 { /* * 對(duì)于多位操作符,需要把單個(gè)字符連接起來(lái)然后作為一個(gè)雙精度數(shù)放入棧中 * 一位數(shù)的操作符直接放入棧即可,注意從字符變成數(shù)字時(shí)要減去48(0的字符型數(shù)據(jù)) */ 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; } /** * 判斷是否是運(yùn)算符 * @param c 當(dāng)前字符 * @return 布爾型結(jié)果 */ 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; } /** * 判斷是否是數(shù)字 * @param c 當(dāng)前字符 * @return 布爾型結(jié)果 */ public boolean checkDigital(char c) { int num = c; num -= 48; if(num >= 0 && num <= 9) return true; else return false; } /** * 判斷即將入棧的優(yōu)先級(jí)是否更高 * @param popOne 棧頂元素 * @param checkOne 即將入棧元素 * @return 布爾型結(jié)果 */ public boolean checkPriority(char popOne,char checkOne) { if((popOne == "*" || popOne == "/") && (checkOne == "+" || checkOne == "-")) return true; else if(popOne == checkOne) return true; else return false; } } 如果我的文章可以幫到您,勞煩您點(diǎn)進(jìn)源碼點(diǎn)個(gè) ★ Star 哦!
https://github.com/Hephaest/S...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69137.html
摘要:代碼實(shí)現(xiàn)在控制臺(tái)打印總結(jié)本篇文章帶大家搭好環(huán)境,并體驗(yàn)了控制臺(tái)打印。輸出結(jié)果總結(jié)熟練掌握取余和整除運(yùn)算,大有作用。終止本次循環(huán),繼續(xù)執(zhí)行下一次循環(huán)。 ?本文收錄...
摘要:因?yàn)楣芾砣藛T是了解手下的人員以及自己負(fù)責(zé)的事情的。處理器優(yōu)化和指令重排上面提到在在和主存之間增加緩存,在多線(xiàn)程場(chǎng)景下會(huì)存在緩存一致性問(wèn)題。有沒(méi)有發(fā)現(xiàn),緩存一致性問(wèn)題其實(shí)就是可見(jiàn)性問(wèn)題。 網(wǎng)上有很多關(guān)于Java內(nèi)存模型的文章,在《深入理解Java虛擬機(jī)》和《Java并發(fā)編程的藝術(shù)》等書(shū)中也都有關(guān)于這個(gè)知識(shí)點(diǎn)的介紹。但是,很多人讀完之后還是搞不清楚,甚至有的人說(shuō)自己更懵了。本文,就來(lái)整體的...
摘要:因?yàn)楣芾砣藛T是了解手下的人員以及自己負(fù)責(zé)的事情的。處理器優(yōu)化和指令重排上面提到在在和主存之間增加緩存,在多線(xiàn)程場(chǎng)景下會(huì)存在緩存一致性問(wèn)題。有沒(méi)有發(fā)現(xiàn),緩存一致性問(wèn)題其實(shí)就是可見(jiàn)性問(wèn)題。 網(wǎng)上有很多關(guān)于Java內(nèi)存模型的文章,在《深入理解Java虛擬機(jī)》和《Java并發(fā)編程的藝術(shù)》等書(shū)中也都有關(guān)于這個(gè)知識(shí)點(diǎn)的介紹。但是,很多人讀完之后還是搞不清楚,甚至有的人說(shuō)自己更懵了。本文,就來(lái)整體的...
摘要:在領(lǐng)域,實(shí)現(xiàn)并發(fā)程序的主要手段就是多線(xiàn)程??蛇\(yùn)行狀態(tài)指的是線(xiàn)程可以分配執(zhí)行。當(dāng)?shù)却氖录霈F(xiàn)了,線(xiàn)程就會(huì)從休眠狀態(tài)轉(zhuǎn)換到可運(yùn)行狀態(tài)。導(dǎo)出線(xiàn)程棧,分析線(xiàn)程狀態(tài)是診斷并發(fā)問(wèn)題的一個(gè)重要工具。 在 Java 領(lǐng)域,實(shí)現(xiàn)并發(fā)程序的主要手段就是多線(xiàn)程。線(xiàn)程是操作系統(tǒng)里的一個(gè)概念,雖然各種不同的開(kāi)發(fā)語(yǔ)言如 Java、C# 等都對(duì)其進(jìn)行了封裝,但原理和思路都是相同都。Java 語(yǔ)言里的線(xiàn)程本質(zhì)上就是...
閱讀 2240·2019-08-30 10:51
閱讀 793·2019-08-30 10:50
閱讀 1475·2019-08-30 10:49
閱讀 3139·2019-08-26 13:55
閱讀 1605·2019-08-26 11:39
閱讀 3419·2019-08-26 11:34
閱讀 1949·2019-08-23 18:30
閱讀 3387·2019-08-23 18:22