/**
* 表达式计算器,输入数学表达式的字符串,输出计算结果的数值
* 1.扫描表达式,将运算符与运算数分别入栈,
* 2.运算符入栈前先与上一个运算符进行优先级比较,如果当前运算符优先级低于或等于前一个运算符,
* 则将前一个运算符和对应的运算数出栈运算,否则运算符直接入栈
* @author lijy
*/
public class ExpressionCaculator
{
/**
* 运算数栈
* */
private var numberStack:Array;
/**
* 运算符栈
* */
private var operatorStack:Array;
/**
* 构造函数
*/
public function ExpressionCaculator(){}
/**
* 表达式计算器
* @expression 表达式
* @return 计算结果,NaN表示格式错误或存在不支持的运算符
* */
public function caculate(expression:String):Number{
//去除空格
var arr:Array = expression.split(' ');
expression = '';
for each(var item:String in arr){
if(item!=' ')expression+=item;
}
//初始化栈
numberStack = new Array();
operatorStack = new Array();
//遍历表达式
for(var i:int=0; i<expression.length; i++){
var index:int = getNextNumberEndIndex(expression,i);
if(index>i){
//获取运算数
var data:Number = Number(expression.substr(i,index-i));
if(isNaN(data)){
//表达式有误返回NaN
return NaN;
}else{
//运算数入栈
numberStack.push(data);
i=index-1;
}
}else{
//获取运算符,并调用出栈运算循环
var nextOperator:String = expression.substr(i,1);
var success:Boolean = this.calculateLoop(nextOperator)
if(!success){
//运算过程中出现不支持的操作符
return NaN;
}else if(nextOperator!=')'){
//操作符入栈,右括号除外
operatorStack.push(nextOperator);
}
}
}
//最后调用出栈运算循环,计算还没有算完的部分
var success:Boolean = this.calculateLoop()
if(!success){
//运算过程中出现不支持的操作符
return NaN;
}else{
//返回计算结果
return numberStack[0];
}
}
/**
* 出栈运算循环:
* 循环出栈之前的运算符与下一个即将入栈的运算符做优先级对比,
* 若优先级高于下一个则进行计算,若为括号则根据情况做括号合并或跳出计算
* @nextOperator 下一个即将入栈的运算符,用于跟之前的运算符做优先级对比或括号消除
* @return 计算是否成功,如果存在不支持的运算符将返回NaN
* */
private function calculateLoop(nextOperator:String=')'):Boolean{
//返回结果初始值:是否存在不支持的运算符
var result:Boolean=true;
//获取下一个运算符的优先级
var nextPriority:Number = getOperatorPriority(nextOperator);
//进入运算循环
while(operatorStack.length>0){
var lastOperator:String = operatorStack[operatorStack.length-1];
var lastPriority:Number = getOperatorPriority(lastOperator);
if(isNaN(lastPriority) || isNaN(nextPriority)){
//存在不支持的运算符
result = false;
break;
}
if(nextPriority > lastPriority){
//优先级大于之前的运算符则不运算,跳出循环
result = true;
break;
}else if(lastOperator=='('){
//如果上一个运算符是左括号:
//1.下一个运算符是右括号,那么左右括号抵消,运算符出站并跳出运算循环
//2.下一个运算符不是右括号,那么左括号保留,跳出运算循环
if(nextOperator==')')operatorStack.pop();
result = true;
break;
}else{
//其他情况出站运算并循环
result = popAndCalculate();
if(result==false){
break;
}
}
}
return result;
}
/**
* 出栈最后一个运算符和对应的运算数进行运算,将运算结果入栈
* @return 运算异常返回false,正常返回true
* */
private function popAndCalculate():Boolean{
if(numberStack.length<2 || operatorStack.length<1){
//至少要有两个运算数和一个运算符
return false;
}else{
var lastOperator:String = operatorStack.pop() as String;
var num1:Number = numberStack.pop() as Number;
var num2:Number = numberStack.pop() as Number;
switch(lastOperator){
case '*':
numberStack.push(num2*num1);
break;
case '/':
numberStack.push(num2/num1);
break;
case '+':
numberStack.push(num2+num1);
break;
case '-':
numberStack.push(num2-num1);
break;
}
return true;
}
}
/**
* 从指定下标位置开始获取下一个运算数结束的下标位置,注意判断减号和负号
* @expression 表达式
* @startIndex 判断的起始下标位置
* @return 下一个运算数结束的下标位置,若返回值与startIndex相等说明下一个字符是操作符不是运算数
* */
private static function getNextNumberEndIndex(expression:String, startIndex:int):int{
var i:int=startIndex;
//判断减号和负号,表达式的第一个字符或者左括号前的第一个字符如果为-,则判定为负号,否则为减号
if(expression.charAt(i)=='-'){
if(i==0 || expression.charAt(i-1)=='('){
i++;
}else{
return i;
}
}
//遍历数字和小数点
for(i; i<expression.length; i++){
var ascii:Number = expression.charCodeAt(i);
if((ascii>=48&&ascii<=57) || ascii==46){
continue;
}else{
return i;
}
}
return i;
}
/**
* 获取运算符优先级
* @operator 运算符
* @return 优先级
* */
private static function getOperatorPriority(operator:String):Number{
switch(operator){
case '(':
return 3;
case '*':
case '/':
return 2;
case '+':
case '-':
return 1;
case ')':
return 0;
default:
return NaN;
}
}
}