首先,看看pear-FSM的自带例子examples\rpn.php
<?php require_once 'FSM.php'; function BeginBuildNumber($symbol, $payload) { array_push($payload, $symbol); } function BuildNumber($symbol, $payload) { $n = array_pop($payload); $n = $n . $symbol; array_push($payload, $n); } function EndBuildNumber($symbol, $payload) { $n = array_pop($payload); array_push($payload, (int)$n); } function DoOperator($symbol, $payload) { $ar = array_pop($payload); $al = array_pop($payload); if ($symbol == '+') { array_push($payload, $al + $ar); } elseif ($symbol == '-') { array_push($payload, $al - $ar); } elseif ($symbol == '*') { array_push($payload, $al * $ar); } elseif ($symbol == '/') { array_push($payload, $al / $ar); } } function DoEqual($symbol, $payload) { 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'); //如果输入符号是"=",则从"INIT"到"INIT"状态,并调用“DoEqual" $fsm->addTransition('=', 'INIT', 'INIT', 'DoEqual'); //如果输入符号是0~9,从"INIT"到”BUILD_NUMBER',调用‘BeginBuildNumber’ $fsm->addTransitions(range(0,9), 'INIT', 'BUILDING_NUMBER', 'BeginBuildNumber'); //如果输入符号是0~9, 则从"BUILDING_NUMBER",到”BUILDING_NUMBER“,调用BuildNumber $fsm->addTransitions(range(0,9), 'BUILDING_NUMBER', 'BUILDING_NUMBER', 'BuildNumber'); //输入为空格就从BUILDING_NUMBER回到INIT,且调用EndBuildNumber $fsm->addTransition(' ', 'BUILDING_NUMBER', 'INIT', 'EndBuildNumber'); //输入为加减乘除就从INIT回到INIT,调用DoOperator $fsm->addTransitions(array('+','-','*','/'), 'INIT', 'INIT', 'DoOperator'); echo "Expression:\n"; $stdin = fopen('php://stdin', 'r'); $expression = rtrim(fgets($stdin)); $symbols = preg_split('//', $expression, -1, PREG_SPLIT_NO_EMPTY); $fsm->processList($symbols);
这个例子没有调用实例,但是可以从代码上看出,这是一个简单的0~9的四则运算表达式计算器
在longlong ago以前,我在数据结构课学习栈和广义表的时候学习过类似手法,下面来分析下这段代码执行后输入四则运算表达式的结果。
1. 输入了0~9,加减乘除之外的符号,则触发Error,到INIT状态
2. INIT可以到INIT
3. 碰到=,DoEqual,然后回到INIT
4. 在INIT和数字输入状态,可输入0~9数字,转到数字输入阶段
5. 在INIT阶段可输入加减乘除,触发DoOperator,然后可以继续运算
6. 空格是结束
经实测,(命令行下 php 你的文件目录\fsm\rpn.php)
这个example有bug,要把
BeginBuildNumber
等几个函数的参数
$payload改成
&$payload然后输入测试的格式是
3 4 + =
中间要有空格。
然后就可以输出个7
如果要进一步观察,print_r下$fsm可大致观察到这个状态机的设计。
然而需要进一步注意的是,在github上有个荷兰人提出
在fsm.php中,process函数中,
$this->_currentState = $transition[0];这句有问题,因为状态转换尚未完成,个人觉得有道理,建议这里改掉。
这里就感觉FSM的例子不是很爽,除了有bug外, 测试例子都是要摸索出来,接下来我来改改这例子,让它好用一些。