【转载】php简单实现算术表达式转换成逆波兰式,并求解

(文章来源:https://my.oschina.net/u/566545/blog/103030 版权归原作者所有)

最近一直在学习C/C++,可学的都是原理语法之类的,没有实战成绩甚是令人不爽。想用C/C++写个计算器一直是我的夙愿,刚敲键盘,就不知可否了,想来想去还是对计算器的算法不是很清楚。由于本人是学php出身,所以先使用php将计算器算法给实现了一下,以便更好的学习C/C++。这个简单的计算器采用的是逆波兰式来做的,仅支持加减乘除四种运算,纯粹个人练习记录一下,还望多多支持。

  • 将算术表达式转换成逆波兰式
  1. 建立运算符栈stackOperator用于运算符的存储,压入'@';建立逆波兰式存储栈stackOut,并置空。
  2. 预处理表达式,正、负号前加0(如果一个加号(减号)出现在最前面或左括号后面,则该加号(减号)为正负号) 。
  3. 顺序扫描表达式,如果当前字符是数字(优先级为0的符号),则直接入栈stackOut;如果当前字符为运算符或括号(优先级不为0的符号),则判断第4点 。
  4. 若当前运算符为'(',直接入栈stackOperator;
    若为')',出栈(stackOperator)并顺序输出运算符直到遇到第一个'(',遇到的第一个'(' 出栈(stackOperator)但不输出;
    若为四则运算符,比较栈顶元素与当前元素的优先级:
    如果栈顶元素运算符优先级 >= 当前元素的优先级, 出栈并顺序输出运算符直到栈顶元素优先级<当前元素优先级,然后将当前元素入栈(stackOperator);
    如果栈顶元素的优先级>当前元素的优先级,直接入栈(stackOperator)。
  5. 重复第3点直到表达式扫描完毕。
  6. 顺序出栈(stackOperator)并将输出的元素压入栈stackOut,直到栈顶元素为'@'。
  • 计算逆波兰式
  1. 准备一个栈stack,并置空。
  2. 顺序读取(从栈底到栈顶)栈stackOut,碰到操作数,入栈stack。
  3. 碰到操作符,stack弹出两个元素,运算并将运算结果入栈stack。
  4. 重复执行2~3步骤,栈stack即是表达式结果。
  • 实现代码
  • <?php
    /**
     * php简单实现算术表达式转换成逆波兰式,并求解。
     * 仅支持加减乘除四种运算
     * @author joe, joenali@163.com
     * @date 2013-01-17
     * <pre>
     *  require 'Calc.php';
     *  $calc = new Calc('(1+9)/2');
     *  echo $calc->getExpression();
     *  echo $calc->calculate();
     * </pre>
     */
    class Calc {
    
        protected $_stackOperator = array('@');
        protected $_stackOut = array();
        protected $_operator = array('@', '(', ')', '+', '-', '*', '/');
        protected $_priority = array('@' => 0, '(' => 10, ')' => 10, '+' => 20, '-' => 20, '*' => 30, '/' => 30);
    
        public function __construct($expression) {
            $this->convert($expression);
        }
    
        /**
         * 解析字符串表达式
         * 解析字符串表达式,将数字和运算符分离,用数组存储
         * @param string $expression
         * @return array
         */
        protected function expressionParase($expression) {
            $arr = str_split($expression);
            $data = $tmp = array();
            do {
                $item = array_shift($arr);
                if (in_array($item, $this->_operator)) {
                    if ($tmp) {
                        array_push($data, implode('', $tmp));
                        $tmp = array();
                    }
                    array_push($data, $item);
                } else {
                    array_push($tmp, $item);
                }
    
            } while(count($arr));
            array_push($data, implode('', $tmp));
            return $data;
        }
    
        /**
         * 生成逆波兰式
         * @param string $expression
         */
        protected function convert($expression) {
            foreach ($this->expressionParase($expression) as $char) {
                if (preg_match("/^[0-9]+$/", $char)) {
                    array_push($this->_stackOut, $char);
                } else if (in_array($char, $this->_operator)) {
                    if ('(' == $char) {
                        array_push($this->_stackOperator, $char);
                    } else if (')' == $char) {
                        while (count($this->_stackOperator) > 1) {
                            $drop = array_pop($this->_stackOperator);
                            if ('(' == $drop) {
                                break;
                            } else {
                                array_push($this->_stackOut, $drop);
                            }
                        }
                    } else {
                        while (count($this->_stackOperator)) {
                            $oTop = end($this->_stackOperator);
                            if ($this->_priority[$char] > $this->_priority[$oTop]) {
                                array_push($this->_stackOperator, $char);
                                break;
                            } else {
                                $drop = array_pop($this->_stackOperator);
                                array_push($this->_stackOut, $drop);
                            }
                        }
                    }
                }
            }
    
            while (count($this->_stackOperator)) {
                $drop = array_pop($this->_stackOperator);
                if ('@' == $drop) {
                    break;
                } else {
                    array_push($this->_stackOut, $drop);
                }
            }
        }
    
        /**
         * 获取逆波兰式
         * @return string
         */
        public function getExpression() {
            return implode('', $this->_stackOut);
        }
    
        /**
         * 计算逆波兰式
         * @return int
         */
        public function calculate() {
            $stack = array();
            foreach ($this->_stackOut as $char) {
                if (preg_match("/^[0-9]+$/", $char)) {
                    array_push($stack, $char);
                } else if (in_array($char, $this->_operator)) {
                    $b = array_pop($stack);
                    $a = array_pop($stack);
    
                    array_push($stack, $this->operator($a, $b, $char));
                }
            }
    
            return end($stack);
        }
    
        protected function operator($a, $b, $o) {
            switch ($o) {
                case '+':
                    return intval($a) + intval($b);
                    break;
                case '+':
                    return intval($a) + intval($b);
                    break;
                case '-':
                    return intval($a) - intval($b);
                    break;
                case '*':
                    return intval($a) * intval($b);
                    break;
                case '/':
                    return intval($a) / intval($b);
                    break;
            }
        }
    }
    

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值