逆波兰表达式 - 计算器(Java语言)

10 篇文章 0 订阅


逆波兰表达式 - 计算器(Java语言)

本文章是继上一篇文章《逆波兰表达式 - 算法描述与实现》所延伸出的对逆波兰算法的应用。
文章中将给出使用Java语言实现的基于逆波兰表达式实现的计算器程序的完整代码和详细注释。


源代码

Calculate.java 计算器类

package pers.landriesnidis.calculate;

import java.util.LinkedList;
import java.util.Stack;

import pers.landriesnidis.calculate.exception.InvalidExpressionException;
import pers.landriesnidis.calculate.exception.NumberFormatException;
import pers.landriesnidis.calculate.exception.UnknownOperatorException;

public class Calculate {
	
	public static void main(String[] args) throws NumberFormatException, InvalidExpressionException, UnknownOperatorException {
		
		if(args.length==0) args = new String[]{"2*3.14*(-2+10)"};
		
		if(args.length>0){
			System.out.println(operation(args[0]));
		}else{
			System.out.println("no expression.");
		}
	}
	
	/**
	 * 表达式补全
	 * 由于“+”、“-”可作为单目表达式使用(如:-3+2),因此通过预处理为表达式在不影响结果的前提下添加0,使其保证能有二元进行运算
	 * 示例:
	 * 8*-1.25+12 => 8*(0-1.25)+12
	 * @param expression 待处理的表达式
	 * @return 处理后的表达式
	 */
	private static String expressionCompletion(String expression){
		StringBuilder sb = new StringBuilder(expression);
		int len = sb.length();
		boolean b = false;
		for(int i=0;i<len;++i){
			char c = sb.charAt(i);
			b = false;
			
			if(c=='-' || c=='+'){
				if(i==0){
					sb.insert(0, "(0");
					b=true;
				}else{
					if(RPN.isOperator(sb.charAt(i-1)) && sb.charAt(i-1)!=')'){
						sb.insert(i, "(0");
						b=true;
					}
				}
				if(b){
					len+=2;
					for(int j=i+2;i<len;++j){
						if(RPN.isOperator(sb.charAt(j))){
							sb.insert(j, ')');
							++len;
							break;
						}
					}
					
				}
			}
		}
		return sb.toString();
	}
	
	/**
	 * 表达式计算
	 * 先将中缀表达式转换为后缀表达式,再计算表达式的结果
	 * @param expression 表达式
	 * @return 运算结果
	 * @throws NumberFormatException 数值非浮点型
	 * @throws InvalidExpressionException 表达式无效
	 * @throws UnknownOperatorException	未知的运算符
	 */
	public static double operation(String expression) throws NumberFormatException, InvalidExpressionException, UnknownOperatorException{
		
		// 预处理
		expression = expressionCompletion(expression);
		
		// 使用逆波兰算法处理
		LinkedList<String> rpnList = RPN.parse(expression);
		
		// 保存每一步运算结果的操作数栈
		Stack<Double> operands = new Stack<Double>();
		
		// 若表达式第一位为运算符,则表达式无效
		if(RPN.isOperator(rpnList.get(0).charAt(0))) throw new InvalidExpressionException(expression);
		
		// 遍历逆波兰表达式中每一项元素
		for(String elem:rpnList){
			
			// 若是运算符
			if(RPN.isOperator(elem.charAt(0))){
				
				// 不满足二元运算抛出异常
				if(operands.size()<2) throw new InvalidExpressionException(expression);
				
				// 从操作数栈取出栈顶的两个操作数
				double value2 = operands.pop();
				double value1 = operands.pop();
				
				// 获得运算结果
				double result = binaryOperation(elem.charAt(0),value1,value2);
				
				// 将计算结果压栈
				operands.push(result);
				
			// 如果是数值
			}else{
				// 检查数值有效性
				if(!RegexUtils.isValue(elem)) throw new NumberFormatException(elem);
				operands.push(Double.parseDouble(elem));
			}
		}
		// 如果操作数栈中元素个数不唯一则说明表达式不正确
		if(operands.size()!=1) throw new InvalidExpressionException(expression);
		
		// 返回操作数栈中唯一的元素
		return operands.pop();
	}
	
	/**
	 * 二元运算
	 * @param operator 运算符
	 * @param value1 值1
	 * @param value2 值2
	 * @return 运算结果
	 * @throws UnknownOperatorException 传入未知的运算符
	 */
	private static double binaryOperation(char operator, double value1, double value2) throws UnknownOperatorException{
		switch(operator){
		case '+':
			return value1+value2;
		case '-':
			return value1-value2;
		case '*':
			return value1*value2;
		case '/':
			if(value2==0) throw new ArithmeticException("/ by zero.");
			return value1/value2;
		default:
			throw new UnknownOperatorException(operator);
		}
	}
}	

RPN.java 逆波兰表达式转换工具类

关于逆波兰表达式转换工具类的完整代码和算法描述请查看上一篇文章《逆波兰表达式 - 算法描述与实现》

RegexUtils.java 正则工具类

package pers.landriesnidis.calculate;

import java.util.regex.Pattern;

public class RegexUtils {
	// 正则表达式(浮点数)
	private final static Pattern PATTERN = Pattern.compile("^(-?\\d+)(\\.\\d+)?$");
	
	/**
	 * 判断字符串是否是数值
	 * @param str 
	 * @return
	 */
	public static boolean isValue(String str){
		return PATTERN.matcher(str).matches();
	}
}

自定义异常类

InvalidExpressionException.java 无效表达式异常
package pers.landriesnidis.calculate.exception;

/**
 * 无效表达式异常
 * @author Landriesnidis
 */
public class InvalidExpressionException extends Exception{
	private static final long serialVersionUID = 1L;

	public InvalidExpressionException(String expression) {
		super(String.format("Invalid expression: '%s'.", expression));
	}
}
NumberFormatException.java 数值格式异常
package pers.landriesnidis.calculate.exception;

/**
 * 数值格式异常
 * @author Landriesnidis
 */
public class NumberFormatException extends Exception{
	private static final long serialVersionUID = 1L;

	public NumberFormatException(String numberString) {
		super(String.format("'%s' is not a valid floating-point number.", numberString));
	}
}

UnknownOperatorException.java 未知的运算符异常
package pers.landriesnidis.calculate.exception;

/**
 * 未知的运算符异常
 * @author Landriesnidis
 */
public class UnknownOperatorException extends Exception{
	private static final long serialVersionUID = 1L;

	public UnknownOperatorException(char operator) {
		super(String.format("'%c' is not a valid operator.", operator));
	}
}


测试

为了测试效果更加明显,在每一步计算后打印当前的情况。
用于测试的表达式为:2*3.14*(-2+10)
测试打印输出结果:

原始表达式:2*3.14*(-2+10)
补充完整后:2*3.14*((0-2)+10)
逆波兰转换:[2, 3.14, *, 0, 2, -, 10, +, *]
计算的过程:[6.28, 0, 2, -, 10, +, *]
计算的过程:[6.28, -2.0, 10, +, *]
计算的过程:[6.28, 8.0, *]
计算的过程:[50.24]
50.24

获取源代码

源码已上传至GitHub,点击此处跳转

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿长大人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值