200行Java代码搞定计算器程序
发现了大学时候写的计算器小程序,还有个图形界面,能够图形化展示表达式语法树,哈哈;)
只有200行Java代码,不但能够计算加减乘除,还能够匹配小括号~
代码点评:
从朴素的界面配色到简单易懂错误提示,无不体现了“用户体验”至上的设计理念;代码异常处理全面合理、滴水不漏,代码缩进优雅大方,变量命名直观易懂;再结合长度适中简单明了的注释,程序整体给人一种清新脱俗之感。背后不难看出作者对学习的热爱以及对设计的苛求,工匠精神可见一斑,真可谓是大学数据结构学以致用的典范!
关于数据结构的干货,可参考博主的《深入理解Java集合框架》系列文章,一定不让你失望。

实现算法参考严蔚敏的《数据结构(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);
}
}
200行Java代码搞定计算器程序的更多相关文章
- 5行js代码搞定导航吸顶效果
一.HTML布局 首先写HTML布局 <body> <div id="wrap"></div> </body> 二.CSS样式 给点 ...
- 180行ruby代码搞定游戏2048
最今在玩2048这款小游戏,游戏逻辑简单,很适合我这样的对于游戏新入行的人来实现逻辑.于是选择了最拿手的ruby语言来实现这款小游戏的主要逻辑.还是挺简单的,加起来4小时左右搞定. 上代码: requ ...
- 【备忘】windows环境下20行php代码搞定音频裁剪
先上图,由于最近的需求需要对语音文件进行处理,所以抽空研究了下php处理音/视频文件的处理,简单的demo处理,截取一个音频文件的前20秒,并保存新的媒体文件. 操作步骤: ①在此站点下载所需的辅助程 ...
- 80行Python代码搞定全国区划代码
微信搜索:码农StayUp 主页地址:https://gozhuyinglong.github.io 源码分享:https://github.com/gozhuyinglong/blog-demos ...
- 几行JavaScript代码搞定Iframe 自动适应
场景:Iframe嵌入flash,希望flash能随着页面的resize而resize. 主要代码: 代码 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTM ...
- 几句java代码搞定十万个为什么数据
最近想做一个app,为小朋友推荐十万个为什么的故事,但是找了很久数据,发现挺难的找的,又去写爬虫,发现没有一个好的网站可以爬,最后一个偶然的机会在csdn上发现一个可用的接口,很快就把问题解决了,下面 ...
- 3kb jQuery代码搞定各种树形选择。
自制Jquery树形选择插件. 对付各种树形选择(省市,分类..)90行Jquery代码搞定,少说废话直接上插件代码.稍后介绍使用说明.是之前写的一个插件的精简版. 1.Jquery插件代码 /* * ...
- 30行代码搞定WCF并发性能测试
[以下只是个人观点,欢迎交流] 30行代码搞定WCF并发性能 轻量级测试. 1. 调用并发测试接口 static void Main() { List< ...
- 10行代码搞定移动web端自定义tap事件
发发牢骚 移动web端里摸爬滚打这么久踩了不少坑,有一定移动web端经验的同学一定被click困扰过.我也不列外.一路走来被虐的不行,fastclick.touchend.iscroll什么的都用过, ...
随机推荐
- JNI 对象处理 (转)
JNI 的基本问题就是解决 Java 和 C++ 代码互相调用的通信问题,在 C++ 代码编写过程中最大的问题莫过于适应其中的代码编写规则,C++调用或是返回的内容必须遵守 JVM 和 C++ 代码的 ...
- sklearn中各算法类的fit,fit_transform和transform函数
在使用PCA和NFC中有三个函数fit,fit_transform,transform区分不清各自的功能.通过测试,勉强了解各自的不同,在这里做一些笔记. 1.fit_transform是fit和tr ...
- Spring框架学习之高级依赖关系配置(二)
紧接着上篇内容,本篇文章将主要介绍XML Schema的简化配置和使用SpEL表达式语言来优化我们的配置文件. 一.基于XML Schema的简化配置方式 从Spring2.0以来,Spring支持使 ...
- CIFAR-10 dataset 的下载与使用
基本信息 CIFAR-10 是一个包含60000张图片的数据集.其中每张照片为32*32的彩色照片,每个像素点包括RGB三个数值,数值范围 0 ~ 255. 所有照片分属10个不同的类别,分别是 'a ...
- Appium python自动化测试系列之日志的收集(十二)
13.1 日志的定义 13.1.1 日志的定义 听到日志这个东西可能有的人莫名其妙,第一次接触就会觉得我们为什么要收集日志,即使要收集日志那么我们需要收集哪些日志,日志的作用是什么等等. 其实日志无 ...
- AngularJS学习篇(六)
AngularJS 控制器 AngularJS 应用程序被控制器控制. ng-controller 指令定义了应用程序控制器. 控制器是 JavaScript 对象,由标准的 JavaScript 对 ...
- PHP代码审计---基础
PHP伪协议 PHP伪协议事实上是其支持的协议与封装协议,支持的种类有以下12种. * file:// - 访问本地文件系统 * http:// - 访问 HTTP(s) 网址 * ftp:// - ...
- ML技术 - 特征选择
1. 决策树中的特征选择 分类决策树是一种描述对实例进行分类的树型结构,决策树学习本质上就是从训练数据集中归纳出一组分类规则,而二叉决策树类似于if-else规则.决策树的构建也是非常的简单,首先依据 ...
- 使用java生成mapbox-gl可读的vector tile
概述 mapbox-gl主要数据源来自mapbox vector tile,本文就是要阐述怎样把postgresql中的地理空间数据转换成vector tile,流程图如下: 配置 该工程采用spri ...
- 二、VueJs 填坑日记之基础项目构建
在上一篇文章中,大致介绍了一下本系列博文以及学习vuejs我们需要了解的一些概念,希望大家认真阅读,所谓知己知彼,百战百胜,学习也一样,工欲善其事,必先利其器,要想学好vuejs,那前提的概念一定要熟 ...