# [PHP打野] 对pear-FSM的研究（三）改进pear-FSM的四则运算

2014/12/04 13:31

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);

1. 很像switch case，或者说就是一种变形

2. 适用于输入未知情况比较多，但规则较清晰的问题

3. 需要用一个栈进行数据处理

4. 完全可以有多个FSM在程序中。

5. 需要设计大量的函数，清晰的命名，清晰地理顺。（我就是状态太多觉得麻烦所以停下了）

6. 数学计算只是例子，或许用于文字的解析更加实用，但需要将FSM的状态进行训练和保存（即非人纯手工定义各状态）。

0
2 收藏

0 评论
2 收藏
0