首页 > 代码库 > [PHP打野] 对pear-FSM的研究(三)改进pear-FSM的四则运算

[PHP打野] 对pear-FSM的研究(三)改进pear-FSM的四则运算

pear-FSM自带的例子很不好用,带空格输入还要古怪的输入方式,现在我来改个改进版的。

<?php
/*
 rpnEasy.php
 author: mx  2014-12-04  用fsm实现简单四则运算,不支持先乘除后加减
*/
require_once ‘FSM.php‘;
//操作对象1遇到数字
function BuildSmallNumber($symbol, &$payload)
{
    array_push($payload, $symbol);
}

//操作对象1连续遇到数字
function BuildBigNumber($symbol, &$payload)
{
    $n = array_pop($payload);
    $n = $n . $symbol;
	$n = (int)$n;
    array_push($payload, $n);
}

//遇到运算符
function BuildOp($symbol, &$payload){
	array_push($payload, $symbol);
}

//操作对象2遇到数字
function BuildSmallNumber2($symbol, &$payload)
{
    array_push($payload, $symbol);
}

//操作对象2连续遇到数字
function BuildBigNumber2($symbol, &$payload)
{
    $n = array_pop($payload);
    $n = $n . $symbol;
	$n = (int)$n;
    array_push($payload, $n);
}

// function EndBuildNumber($symbol, &$payload)
// {
    // $n = array_pop($payload);
    // array_push($payload, (int)$n);
// }

function DoEqualTemp($symbol, &$payload)
{
	// 先将栈里的几个计算计算
	//操作对象2
    $ar = array_pop($payload);
	//运算符
	$s = array_pop($payload);
	//操作对象1
    $al = array_pop($payload);

    if ($s == ‘+‘) {
        array_push($payload, $al + $ar);
    } elseif ($s == ‘-‘) {
        array_push($payload, $al - $ar);
    } elseif ($s == ‘*‘) {
        array_push($payload, $al * $ar);
    } elseif ($s == ‘/‘) {
        array_push($payload, $al / $ar);
    }
	//最新运算符压入
	array_push($payload, $symbol);
}

function DoEqual($symbol, &$payload)
{
	if(count($payload)>2){
		//操作对象2
		$ar = array_pop($payload);
		//运算符
		$s = array_pop($payload);
		//操作对象1
		$al = array_pop($payload);

		if ($s == ‘+‘) {
			array_push($payload, $al + $ar);
		} elseif ($s == ‘-‘) {
			array_push($payload, $al - $ar);
		} elseif ($s == ‘*‘) {
			array_push($payload, $al * $ar);
		} elseif ($s == ‘/‘) {
			array_push($payload, $al / $ar);
		}
	}
	echo array_pop($payload) . "\n";
}

function Error($symbol, $payload)
{
    echo "This does not compute: $symbol\n";
}

$stack = array();

$fsm = new FSM(‘INIT‘, $stack);
$fsm->setDefaultTransition(‘INIT‘, ‘Error‘);
//状态:起始读入必须是数字。 数字后面可以跟数字或加减乘除或=, 运算符后必须跟数字。
// $fsm->addTransitionAny(‘INIT‘, ‘INIT‘);
$fsm->addTransitions(range(0,9), ‘INIT‘, ‘INPUT1‘, ‘BuildSmallNumber‘);
$fsm->addTransitions(range(0,9), ‘INPUT1‘, ‘INPUT1‘, ‘BuildBigNumber‘);
$fsm->addTransitions(array(‘+‘,‘-‘,‘*‘,‘/‘), ‘INPUT1‘, ‘OPERATE‘, ‘BuildOp‘);
$fsm->addTransitions(range(0,9), ‘OPERATE‘, ‘INPUT2‘, ‘BuildSmallNumber2‘);
$fsm->addTransitions(range(0,9), ‘INPUT2‘, ‘INPUT2‘, ‘BuildBigNumber2‘);
$fsm->addTransitions(array(‘+‘,‘-‘,‘*‘,‘/‘), ‘INPUT2‘, ‘OPERATE‘, ‘DoEqualTemp‘);
$fsm->addTransition(‘=‘, ‘INPUT2‘, ‘INIT‘, ‘DoEqual‘);

echo "Expression:\n";
$stdin = fopen(‘php://stdin‘, ‘r‘);
$expression = rtrim(fgets($stdin));
$symbols = preg_split(‘//‘, $expression, -1, PREG_SPLIT_NO_EMPTY);

$fsm->processList($symbols);

运行下 php 目录\rpnEasy.php

输入 11+22-3+5之类,测试成功!

这个例子的主要改动在于,原代码是维持栈里只有数字,用空格作为输入结束分隔符,碰到符号开始算,于是就要输入3 4 + 这样的格式。 现在改成 区分 操作对象1,运算符,操作对象2 (即对应原代码里al,ar象征的左右值),把运算符也暂存到栈里,则可支持连续不间断的紧密计算表达式。

但是,这个是坚持从左到右的平滑计算,如果要先乘除后加减带或者先加减或乘除,运算符有先后优先级的呢?




[PHP打野] 对pear-FSM的研究(三)改进pear-FSM的四则运算