1.实现原理:
说明:在我原来做的逆波兰计算器中,postfix是用来表示逆波兰表达式的。但是和buptpatriot讨论后,想直接实现,下面是直接实现的方法:
首先,定义优先级:
function level($op) {
switch ($op) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
}
}
之后,扫描输入序列,读取输入的字符串,把其中的数字(可能是浮点型,负数)和操作符提取出来
收集浮点数:
function readin($expr) {
$expr = str_split($expr);
$len = count($expr);
$infix = array();
$opts = array('+','-','*','/','(',')');
$num = "";
for($i = 0; $i < $len; $i++) {
if(in_array($expr[$i], $opts) || ($i == $len-1)) {
if($num = floatval($num)) {
array_push($infix, $num);
$num = "";
}
if($expr[$i] == '-' && ($expr[$i-1] == NULL || $expr[$i-1] == '(')
&& (is_numeric($expr[$i+1]) || $expr[$i+1] == '(')) {
$expr[$i] = '^'; //unary negotion
}
array_push($infix, $expr[$i]);
}
else {
$num .= $expr[$i];
}
}
var_dump($infix);
return $infix;
}
对于否定符号(-)做处理:
if($expr[$i] == '-' && ($expr[$i-1] == NULL || $expr[$i-1] == '(')
&& (is_numeric($expr[$i+1]) || $expr[$i+1] == '(')) {
$expr[$i] = '^';
}
此时,否定符号'-'转化成'^'
1) 如果是数字,则存入栈 postfix 中;
2) 如果是运算符:
2.1)是 ‘(’ ,则存入栈 stack 中;
2.2)是‘+’,‘-’,‘*’,‘/’,‘’,则检查栈stack是否为空:
2.2.1)stack 为空:则存入栈内
2.2.2 ) stack不为空,执行下面的操作:
从stack中弹出一个元素op
如果这个元素的不是 ‘(’ 且弹出元素的优先级大于当前扫描元素的优先级,就将弹出元素存入postfix栈内,同时从postfix中弹出2个元素a,b,计算a op b的结 果,将其存入postfix中;
否则,把弹出元素op重新压入栈stack内
最后,当前扫描到的运算符入栈stack
2.3) 是‘^’ :执行下面的操作:
从postfix中弹出1个元素a,将(0-a)存入postfix中;
2.4) 是‘)’ :执行下面的操作:
弹出元素存入postfix栈内,同时从postfix中弹出2个元素a,b,计 算a op b的结果,将其存入postfix中;
直到弹出元素为'(';
画一个表来看:4/((3-1)*2) = 1的计算过程
current | stack | postfix |
4 | empty | 4 |
/ | / | 4 |
( | /( | 4 |
( | /(( | 4 |
3 | /(( | 43 |
- | /((- | 43 |
1 | /((- | 431 |
) | /( | 42 |
* | /(* | 42 |
2 | /(* | 422 |
) | / | 44 |
empty | 1(result) |
2.php 代码:
<html>
<head>
<title>Reverse Polish Notation Calculator</title>
</head>
<body>
<h1>Calculator</h1>
<h3>by wusuopubupt</h3>
<form id="my_form" method="POST" action="<?php echo $_SERVER['SCRIPT_NAME'];?>">
<input type="text" name="expr" placeholder="input expression to calculate!"/>
</form>
<?php
if(isset($_POST['expr'])) {
$expr = readin($_POST['expr']);
$result = cal($expr);
echo "result is:";
var_dump($result);
}
function readin($expr) {
$expr = str_split($expr);
$len = count($expr);
$infix = array();
$opts = array('+','-','*','/','(',')');
$num = "";
for($i = 0; $i < $len; $i++) {
if(in_array($expr[$i], $opts) || ($i == $len-1)) {
if($num = floatval($num)) {
array_push($infix, $num);
$num = "";
}
if($expr[$i] == '-' && ($expr[$i-1] == NULL || $expr[$i-1] == '(') && (is_numeric($expr[$i+1]) || $expr[$i+1] == '(')) {
$expr[$i] = '^';
}
array_push($infix, $expr[$i]);
}
else {
$num .= $expr[$i];
}
}
var_dump($infix);
return $infix;
}
function cal($expr) {
$postfix_expr = infix2postfix($expr);
var_dump($postfix_expr);
$len = count($postfix_expr);
$stack = array();
$result = 0;
for($i = 0; $i < $len; $i++) {
$var = $postfix_expr[$i];
if(is_numeric($var)) {
array_push($stack, $var);
}
else {
if($var != '^') {
$rhs = array_pop($stack); //right hand side
$lhs = array_pop($stack); //left hand side
array_push($stack,operate($lhs,$var,$rhs));
}
else {
$rhs = 0 - array_pop($stack);
array_push($stack, $rhs);
}
}
}
return array_pop($stack);
}
function infix2postfix($expr) {
$len = count($expr);
$stack = array();
$postfix = array();
for($i = 0; $i < $len; $i++) {
if(is_numeric($expr[$i])) {
$postfix[] = $expr[$i];
}
else {
if($expr[$i] == '(') {
array_push($stack, $expr[$i]);
}
if($expr[$i] == '+' || $expr[$i] == '-' || $expr[$i] == '*' || $expr[$i] == '/' || $expr[$i] == '^') {
if(!empty($stack)){
$op = array_pop($stack);
if($op != '(' && (level($op) >= level($expr[$i]))) {
array_push($postfix,$op);
}
else {
array_push($stack, $op);
}
}
array_push($stack, $expr[$i]);
}
if($expr[$i] == ')') {
while(($op = array_pop($stack)) != '(') {
array_push($postfix,$op);
}
}
}
}
while(($last_op = array_pop($stack)) != NULL){
array_push($postfix, $last_op);
}
return $postfix;
}
function operate($left,$op,$right) {
switch($op) {
case '+':
return $left + $right;
case '-':
return $left - $right;
case '*':
return $left * $right;
case '/':
return $left / $right;
}
}
function level($op) {
switch ($op) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
}
}
?>
</body>
</html>
3.运行结果:
输入:4/((3-1)*2)
输出:
包含 否定符号的结果:
输入:-(-1.2+1.8)/(1/3.0)
输出: