/** * 表达式计算器,输入数学表达式的字符串,输出计算结果的数值 * 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; } } }