逆波蘭表示法(RPN,後綴表達式)無需括號即可明確運算優先序,適合電腦解析執行。本文展示如何將中序表達式(如 (A*(B+C)-E+F)*G)轉換為 RPN,並以堆疊機制完成計算,適用於薪資公式、報表引擎等動態運算場景。
#)、輸出堆疊 S2#),將 S1 剩餘運算符彈出至 S2math_rpn<?php
class math_rpn {
private $_expression = '';
private $_rpnexp = [];
private $_stack = ['#'];
private $_priority = ['#' => 0, '(' => 10, '+' => 20, '-' => 20, '*' => 30, '/' => 30];
private $_operator = ['(', '+', '-', '*', '/', ')'];
public function __construct($expression) {
$this->_expression = $expression;
}
public function exp2rpn() {
$len = strlen($this->_expression);
for ($i = 0; $i < $len; $i++) {
$char = substr($this->_expression, $i, 1);
if ($char == '(') {
$this->_stack[] = $char;
continue;
} elseif (!in_array($char, $this->_operator)) {
$this->_rpnexp[] = $char;
continue;
} elseif ($char == ')') {
for ($j = count($this->_stack); $j >= 0; $j--) {
$tmp = array_pop($this->_stack);
if ($tmp == "(") break;
$this->_rpnexp[] = $tmp;
}
continue;
} elseif ($this->_priority[$char] <= $this->_priority[end($this->_stack)]) {
$this->_rpnexp[] = array_pop($this->_stack);
$this->_stack[] = $char;
continue;
} else {
$this->_stack[] = $char;
continue;
}
}
for ($i = count($this->_stack); $i >= 0; $i--) {
if (end($this->_stack) == '#') break;
$this->_rpnexp[] = array_pop($this->_stack);
}
return $this->_rpnexp;
}
}
// 測試
$expression = "(A*(B+C)-E+F)*G";
var_dump($expression);
$mathrpn = new math_rpn($expression);
var_dump($mathrpn->exp2rpn());
/* 輸出範例:
string(17) "(A*(B+C)-E+F)*G"
array(7) {
[0]=> string(1) "A"
[1]=> string(1) "B"
[2]=> string(1) "C"
[3]=> string(1) "+"
[4]=> string(1) "*"
[5]=> string(1) "E"
[6]=> string(1) "-"
...
} */
?>
取得 RPN 陣列後,再以一個堆疊掃描:遇運算元入堆疊,遇運算符彈出兩個運算元運算並將結果入堆疊,最終堆疊頂即為運算結果。此處略過計算類別,開發者可依需求擴展支援變數替換、函數呼叫等。
逆波蘭式將複雜的優先序邏輯轉化為簡單的堆疊操作,是實現動態運算引擎的經典選擇。