微信号:importnew

介绍:伯乐在线旗下账号,专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等.

200 行 Java 代码搞定计算器程序

2018-01-12 18:41 ImportNew

(点击上方公众号,可快速关注)


来源:CarpenterLee,

www.cnblogs.com/CarpenterLee/p/8045117.html


发现了大学时候写的计算器小程序,还有个图形界面,能够图形化展示表达式语法树。


只有200行Java代码,不但能够计算加减乘除,还能够匹配小括号~


代码点评:


从朴素的界面配色到简单易懂错误提示,无不体现了“用户体验”至上的设计理念;代码异常处理全面合理、滴水不漏,代码缩进优雅大方,变量命名直观易懂;再结合长度适中简单明了的注释,程序整体给人一种清新脱俗之感。背后不难看出作者对学习的热爱以及对设计的苛求,工匠精神可见一斑,真可谓是大学数据结构学以致用的典范!


关于数据结构的干货,可参考博主的《深入理解Java集合框架》系列文章,一定不让你失望。


http://www.cnblogs.com/CarpenterLee/p/5545987.html



实现算法参考严蔚敏的《数据结构(C语言版)》第三章“栈和队列”,3.2.5节“表达式求值”。


import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.Graphics;

import java.awt.Point;

import java.awt.TextField;

import java.awt.event.KeyAdapter;

import java.awt.event.KeyEvent;

import java.util.Stack;

 

import javax.swing.JFrame;

 

/**

 * 图形界面的计算器程序,只能计算加减乘除,

 * 算式中可以有小括号。数字可以是小数

 */

public class CalcGUI extends JFrame{

    private static final long serialVersionUID = 1L;

    private TreeNode resultTree;

    private String textFieldString;

    private boolean calcSuccess = true;

    private char ops[][] = {

            {'>', '>', '<', '<', '<', '>', '>'},

            {'>', '>', '<', '<', '<', '>', '>'},

            {'>', '>', '>', '>', '<', '>', '>'},

            {'>', '>', '>', '>', '<', '>', '>'},

            {'<', '<', '<', '<', '<', '=', 'E'},

            {'E', 'E', 'E', 'E', 'E', 'E', 'E'},

            {'<', '<', '<', '<', '<', 'E', '='},

    };

    Stack<TreeNode> nodesStack = new Stack<TreeNode>();

    Stack<Character> opsStack = new Stack<Character>();

    public static void main(String[] args) {

        CalcGUI gui = new CalcGUI();

        gui.userGUI();

    }

    public void userGUI(){

        this.setLayout(new BorderLayout());

        TextField tf = new TextField("请输入表达式,按Enter开始计算~", 40);

        tf.selectAll();

        tf.getText();

        tf.addKeyListener(new KeyAdapter(){

            public void keyPressed(KeyEvent e){

                if(e.getKeyCode() == KeyEvent.VK_ENTER){

                    textFieldString = ((TextField)e.getComponent()).getText();

                    calcSuccess = true;

                    resultTree = null;

                    try{

                        resultTree = calc(textFieldString + "#");

                    }catch(Exception e1){

                        calcSuccess = false;

                    }

                    CalcGUI.this.repaint();

                }

            }

        });

        this.add(tf, BorderLayout.NORTH);

        this.setSize(500, 500);

        this.setTitle("calc GUI");

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        this.setResizable(true);

        this.setVisible(true);

    }

    private int levelHeight = 60;

    private int diameter  = 25;

    public void paint(Graphics g){

        super.paint(g);

        if(calcSuccess){

            if(resultTree != null){

                g.drawString("计算结果为:" + resultTree.value, 10, 80);

                int rootBeginX = this.getWidth() / 2;

                int rootBeginY = 100;

                Point p = new Point(rootBeginX, rootBeginY);

                drawTree(g, resultTree, p, this.getWidth() / 2 - 20, p);

            }

        }else{

            g.setColor(Color.RED);

            g.drawString("表达式语法有误!", 10, 80);

        }

    }

    private void drawCircle(Graphics g, Point p, int r){

        g.drawOval(p.x - r, p.y - r, r * 2, r * 2);

    }

    private void drawTree(Graphics g, TreeNode node, Point pme, int width, Point pfather){

        if(node == null) return;

//      System.out.println("in drawTree, node.value=" + node.value + ",node.op=" + node.op);

        g.setColor(Color.GREEN);

        this.drawCircle(g, pme, diameter / 2);

        g.drawLine(pme.x, pme.y, pfather.x, pfather.y);

        if(node.op != 'E'){

            g.setColor(Color.BLACK);

            g.drawString(String.valueOf(node.op), pme.x, pme.y);

        }else{

            g.setColor(Color.BLACK);

            g.drawString(String.valueOf(node.value), pme.x - diameter / 2, pme.y);

        }

        drawTree(g, node.lft, new Point(pme.x - width / 2, pme.y + levelHeight), width / 2, pme);

        drawTree(g, node.rt, new Point(pme.x + width / 2, pme.y + levelHeight), width / 2, pme);

    }

    public TreeNode calc(String inStr) throws Exception{

        opsStack.push('#');

        StringBuilder buf = new StringBuilder();

        int i = 0;

        while(i < inStr.length()){

            if(Character.isDigit(inStr.charAt(i)) || inStr.charAt(i) == '.'){// number

                buf.delete(0, buf.length());

                while(i < inStr.length() && 

                        (Character.isDigit(inStr.charAt(i)) || inStr.charAt(i) == '.'))

                    buf.append(inStr.charAt(i++));

                Double number = Double.parseDouble(buf.toString());

                nodesStack.push(new TreeNode(number));

            }else if(inStr.charAt(i) == ' '){

                i++;

                continue;

            }else{// operation

                char op = inStr.charAt(i);

                int subNew = getSub(op);

                boolean goOn = true;

                while(goOn){

                    if(opsStack.isEmpty())

                        throw new Exception("运算符太少!");

                    char opFormer = opsStack.peek();

                    int subFormer = getSub(opFormer);

                    switch(ops[subFormer][subNew]){

                    case '=':

                        goOn = false;

                        opsStack.pop();

                        break;

                    case '<':

                        goOn = false;

                        opsStack.push(op);

                        break;

                    case '>':

                        goOn = true;

                        TreeNode n1 = nodesStack.pop();

                        TreeNode n0 = nodesStack.pop();

                        double rs = doOperate(n0.value, n1.value, opFormer);

                        nodesStack.push(new TreeNode(rs, opFormer, n0, n1));

                        opsStack.pop();

                        break;

                    default:

                        throw new Exception("没有匹配的操作符:" + op);

                    }

                }

                i++;

            }

        }

        return nodesStack.pop();

         

    }

    private double doOperate(double n0, double n1, char op) throws Exception{

        switch(op){

        case '+': return n0 + n1;

        case '-': return n0 - n1;

        case '*': return n0 * n1;

        case '/': return n0 / n1;

        default: throw new Exception("非法操作符:" + op);

        }

    }

    private int getSub(char c){

        switch(c){

            case '+': return 0;

            case '-': return 1;

            case '*': return 2;

            case '/': return 3;

            case '(': return 4;

            case ')': return 5;

            case '#': return 6;

            default : return -1;

        }

    }

}

class TreeNode{

    public double value;

    public char op = 'E';

    public TreeNode lft;

    public TreeNode rt;

    public TreeNode(double value){

        this.value = value;

    }

    public TreeNode(double value, char op, TreeNode lft, TreeNode rt){

        this.value = value;

        this.op = op;

        this.lft = lft;

        this.rt = rt;

    }

    StringBuilder buf = new StringBuilder();

    public String toString(){

        out(this);

        return buf.toString();

    }

    private void out(TreeNode node){

        if(node == null) return;

        out(node.lft);

        if(node.op != 'E')

            buf.append(node.op);

        else

            buf.append(node.value);

        out(node.rt);

    }

}


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

 
ImportNew 更多文章 改善代码可测性的若干技巧 Java8 Map 示例:一个略复杂的数据映射聚合例子及代码重构 没有 Redis 也能支撑 “ 小米在印度把亚马逊搞挂了 ” 事件的 大型网站系统与 Java 中间件实践 如何编写相对标准的后端项目 (一)组织与运行
猜您喜欢 JS实现无限划动的图片全屏浏览 深度 | Cassandra数据模型设计最佳实践 Python版本的报数游戏 Hadoop 2.7.3 HA安装指南 集群负载均衡技术概述