刚接到一个应用,需要根据给出的公式计算结果值。
参考了AWK写的PERL语言的一个例子 http://www.chinaunix.net/jh/24/738738.html
改编为PHP 的。
所参考的例子中没有 if($isof==1)array_push($OF,array_pop($OF).$a); //如果是连续数字,将上一数字出栈与当前数字组合再入栈: {1,2} = {12}
这一句,没有读懂例子中连续数字是如何处理的。
因我的应用中,表达式中还需可以用自订函数( 将计算值做特殊的取位处理后再继续与其他运算符计算),所以依样画瓢增加了中括号[]的处理,将中括号内的计算值丢进自订函数 pos_num 处理后再返回计算。只要你的表达式中不含中括号,以下代码不修改一样可以计算出正确结果。你也可以将有注释的与中括号处理有关的行及pos_num 函数删除。
目前此代码只能处理 +-*/% () ,数学函数不支持,因我目前的应用没有用到。
如果要支持,也有想到解决办法:判断数字/运算符处再增加字母的判断与处理。
当然,如果您的应用中不需要用到自订函数,只有单纯的+-*/% ()甚至基本的数学函数,你也可以用这个方案:SQL 方法解决表达式求值问题
< ?php
class exp2value{//表达式求值
var $symbol="+-*%/()[]@"; //运算符集合 @ 为计算中使用的辅助符号
//构造运算符优先级关系
//不使用中括号[]特殊处理函数的可删除含有[]的元素
var $oppr=array(
"++"=>">","+-"=>">","+*"=>"<","+/"=>"<","+%"=>"<","+("=>"<","+)"=>">","+["=>">","+]"=>">","+@"=>">",
"-+"=>">","--"=>">","-*"=>"<","-/"=>"<","-%"=>"<","-("=>"<","-)"=>">","-["=>">","-]"=>">","-@"=>">",
"*+"=>">","*-"=>">","**"=>">","*/"=>">","*%"=>"<","*("=>"<","*)"=>">","*["=>">","*]"=>">","*@"=>">",
"/+"=>">","/-"=>">","/*"=>">","//"=>">","/%"=>"<","/("=>"<","/)"=>">","/["=>">","/]"=>">","/@"=>">",
"%+"=>">","%-"=>">","%*"=>">","%/"=>">","%%"=>">","%("=>"<","%)"=>">","%["=>">","%]"=>">","%@"=>">",
"(+"=>"<","(-"=>"<","(*"=>"<","(/"=>"<","(%"=>"<","(("=>"<","()"=>"=","(["=>">",
")+"=>">",")-"=>">",")*"=>">",")/"=>">",")%"=>">","))"=>">",")]"=>">",")@"=>">",
"[+"=>"<","[-"=>"<","[*"=>"<","[/"=>"<","[%"=>"<","[("=>"<","[)"=>"<","[["=>"<","[]"=>"=",
"]+"=>">","]-"=>">","]*"=>">","]/"=>">","]%"=>">","])"=>">","]]"=>">","]@"=>">",
"@+"=>"<","@-"=>"<","@*"=>"<","@/"=>"<","@("=>"<","@%"=>"<","@["=>"<","@@"=>"="
);
var $debug=0; //调试模式
function exp2value($d=0){ //传入非0的参数则开启调试模式
$this->debug=$d;
}
function exp2v($o_string){
$symbol=str_split($this->symbol);
$s=array(); //字符串
$OP=array("@"); //运算符
$OF=array(); //数字
$o_string=str_replace(" ","",$o_string); //去除表达式中的空格
$string=$o_string."@"; //表达式最后加一字符"@" 计算辅助符号
$s=str_split($string);
$i=0;
$m=0;
$isof=0; //连续数字判断
while($s[$i]!="@" || end($OP)!="@"){
$m++;
if($m>300)return "表达式长度超过300";
$a=$s[$i];
if($this->debug<>0)echo "
".$m.":".substr($string,0,$i+1)."
";
if($this->debug<>0)echo "判断字符:".$a." 为";
if(!in_array($a,$symbol)){
if($this->debug<>0)echo "数字";
if($isof==1){
array_push($OF,array_pop($OF).$a); //如果是连续数字,将上一数字出栈与当前数字组合再入栈: {1,2} = {12}
}else{
array_push($OF,$a); //入栈
}
$isof=1;
$i++;
}else{
$isof=0;
if($this->debug<>0)echo "运算符 ";
$t=$this->oppr[end($OP).$s[$i]];//比较优先级别,可再增加异常判断
if($this->debug<>0)echo end($OP).$s[$i];
if($t=="<"){
if($this->debug<>0)echo " 小于";
array_push($OP,$s[$i]);
$i++;
}else if($t=="="){
if($this->debug<>0)echo " 等于";
if(end($OP)=="[" && $s[$i]=="]")array_push($OF,$this->pos_num(array_pop($OF))); //不使用中括号[]特殊处理函数的可删除此行
array_pop($OP);
$i++;
}else{
if($this->debug<>0)echo " 大于";
$str=array_pop($OP);
$y=array_pop($OF);
$x=array_pop($OF);
$r=$this->calculate($x,$str,$y);
if($this->debug<>0)echo "
计算式:".$x.$str.$y."=".$r;
array_push($OF,$r);
}
}
if($this->debug<>0)ECHO "
数字:";
if($this->debug<>0)PRINT_R($OF);
if($this->debug<>0)ECHO "
运算符:";
if($this->debug<>0)PRINT_R($OP);
}
if($this->debug<>0)echo "
最终结果为:".$o_string."=".end($OF)."
";
return end($OF);
}
function calculate($x,$st,$y){ //二元运算函数
if($st=="+")
return $x+$y;
else if($st=="-")
return $x-$y;
else if($st=="*")
return $x*$y;
else if($st=="%")
return $x%$y;
else if($st=="/")
return $x/$y;
}
function pos_num($m){ //对家具尺寸结算结果的个位数取数为0或5:012 34567 89
$m=round($m);//四舍五入为整数
$i=$m%10; //取个位数
switch($i){
case '0':
case '1':
case '2':
$n=$m-$i; //0
break;
case '3':
case '4':
case '5':
case '6':
case '7':
$n=$m-$i+5; //5
break;
case '8':
case '9':
$n=$m-$i+10; //0
break;
default:
$n=$m;
break;
}
return $n;
}
}//对象定义结束
$cc=new exp2value(1); //$cc=new exp2value(); 不显示调试信息
echo $cc->exp2v("[12.1*2]"); //结果为25
echo $cc->exp2v("(12.1*2)"); //结果为24.2
?>
这里可以下载完整代码:
class_exp2value.zip
日, 22.05.2011 05:54
键盘那不是很简单,排线先插好就 可以了