[PHP打野] 對pear-FSM的研究(三)改進pear-FSM的四則運算

pear-FSM自帶的例子很很差用,帶空格輸入還要古怪的輸入方式,如今我來改個改進版的。 php

<?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 express

輸入 11+22-3+5之類,測試成功! 函數

這個例子的主要改動在於,原代碼是維持棧裏只有數字,用空格做爲輸入結束分隔符,碰到符號開始算,因而就要輸入3 4 + 這樣的格式。 如今改爲 區分 操做對象1,運算符,操做對象2 (即對應原代碼裏al,ar象徵的左右值),把運算符也暫存到棧裏,則可支持連續不間斷的緊密計算表達式。 測試

可是,這個是堅持從左到右的平滑計算,若是要先乘除後加減帶或者先加減或乘除,運算符有前後優先級的呢? ui

須要引入一張可定義符號優先級的表,而後修改程序爲每次遇到兩個運算符時,進行與前一次運算符的比較操做。根據算符優先法,用棧完成。 spa


到這裏我停下了,儘管帶運算優先級的基於FSM的計算並不是不可實現,但我沒有繼續去實現。這裏我感受主要仍是依賴於棧的處理,FSM已經不是關鍵。 設計

在這裏總結下我對php-FSM的特性理解: code

1. 很像switch case,或者說就是一種變形 對象

2. 適用於輸入未知狀況比較多,但規則較清晰的問題 get

3. 須要用一個棧進行數據處理

4. 徹底能夠有多個FSM在程序中。

5. 須要設計大量的函數,清晰的命名,清晰地理順。(我就是狀態太多以爲麻煩因此停下了)

6. 數學計算只是例子,或許用於文字的解析更加實用,但須要將FSM的狀態進行訓練和保存(即非人純手工定義各狀態)。

相關文章
相關標籤/搜索