1.是什么不是什么
自己理解,有限自动机是一种数学模型,是自动机的一种,是一种方法也可以理解为工具,是一种理论可以应用用到编程中。不是编程特有的理论,数电里面好多应用,不是一种设计模式,状态模式可以体现这种思想。有些类似UML有自己的规定和表示图形,不过是解决特定问题的。2.有限状态机编程
(英语:FSM-based programming)大致上等同于自动机编程,但有限状态机编程专指以有限状态机为模型的程序。
自动机编程有以下的二项特征:1).程序运行的时间中可以清楚划分成数个自动机的步骤(step),每一个步骤即为一个程序区段,有单一的进入点,可以是一个函数或其他程序。若有需要时,程序区段可以再依其状态的不同,划分为子区段。
2).不同步骤的程序区段只能通过一组清楚标示的变量交换信息,这些变量称为状态(state),使用自动机编程的程序不能用其他不显然可见的方式标示状态,例如区域变量的数值、回传地址、目前程序指针的位置等。因此一程序在任二个不同时间下的差异,只有状态数值的不同,其余都相同。
3.有限状态机编程的实例
这一部分主要参考wiki上http://zh.wikipedia.org/wiki/%E5%9F%BA%E4%BA%8E%E8%87%AA%E5%8A%A8%E6%9C%BA%E7%BC%96%E7%A8%8B。考虑一个C语言的程序,由标准输入流一行一行的读取数据,打印各一行的第一个英文单字。因此一开始需确认第一个英文单字之前是否有空白,若有,需读取所有空白后略过不打印,读取第一个英文单字然后打印,之后读取其他内容略过不打印,直到读到换行符号为止。任何情形下只要读到换行符号,就重新开始此算法,任何情形下只要读到文件退出(end-of-file)的符号,就退出程序。EOF即在程序运行过程中按下ctrl+z。
#include <stdio.h>
int main(void)
{
int c;
do {
c = getchar();
while(c == ' ')
c = getchar();
while(c != EOF && c != ' ' && c != '\n') {
putchar(c);
c = getchar();
}
putchar('\n');
while(c != EOF && c != '\n')
c = getchar();
} while(c != EOF);
return 0;
}
上述问题也可以用有有限状态机的方式处理,此程序有三个不同的阶段:读取并跳过第一个单字前的空白、读取第一个单字并且打印、跳过后续的所有字符。以下将这三个阶段定义为三个状态before、inside及after。自动机编程的程序如下:
#include <stdio.h>
int main(void)
{
enum states {
before, inside, after
} state;
int c;
state = before;
while((c = getchar()) != EOF) {
switch(state) {
case before:
if(c == '\n') {
putchar('\n');
} else
if(c != ' ') {
putchar(c);
state = inside;
}
break;
case inside:
switch(c) {
case ' ': state = after; break;
case '\n':
putchar('\n');
state = before;
break;
default: putchar(c);
}
break;
case after:
if(c == '\n') {
putchar('\n');
state = before;
}
}
}
return 0;
}
虽然此程序较长,至少有一个明显的好处,程序中只调用一个读取字符的getchar()函数,而且程序中只有一个循环,不像之前程序使用四个循环。
此程序中while循环内的程序即为自动机的步骤,而循环本身即可重复的运行自动机的程序。
此程序实现如图所示的有限状态机,其中N表示换行字符、S表示空白、A表示其他的字符。自动机依目前状态及读取的字符不同,会运行图中一个箭头所示的动作,可能是由一个状态跳到下一个状态,也者停在原来的状态。其中有些箭头有标示星号,表示需打印读到的字符。
自动机编程中,不一定要为每一个状态撰写独立的处理程序,而且有时状态是由许多变量组成,无法针对每一个状态规划个别的处理程序。此想法有时有助于程序的精简,例如在上述程序中,不论是在哪一个状态,针对换行字符的处理都一様,因此程序可以先处理换行字符,其他输入字符时才依不同状态进行处理,简化后变成以下的程序:
#include <stdio.h>
int main(void)
{
enum states {
before, inside, after
} state;
int c;
state = before;
while((c = getchar()) != EOF) {
if(c == '\n') {
putchar('\n');
state = before;
} else
switch(state) {
case before:
if(c != ' ') {
putchar(c);
state = inside;
}
break;
case inside:
if(c == ' ') {
state = after;
} else {
putchar(c);
}
break;
case after:
break;
}
}
return 0;
}
上述程序的一个重要特点是自动机步骤的程序区块都只使用区域变量,以下的例子将自动机步骤集成为一个独立的函数step(),更可以突显上述的特点:
#include <stdio.h>
enum states { before, inside, after };
void step(enum states *state, int c)
{
if(c == '\n') {
putchar('\n');
*state = before;
} else
switch(*state) {
case before:
if(c != ' ') {
putchar(c);
*state = inside;
}
break;
case inside:
if(c == ' ') {
*state = after;
} else {
putchar(c);
}
break;
case after:
break;
}
}
int main(void)
{
int c;
enum states state = before;
while((c = getchar()) != EOF) {
step(&state, c);
}
return 0;
}
此例清楚的呈现自动机编程程序的基本特点:
1).各自动机步骤程序的运行时间不互相重叠。
2).前一个步骤和下一个步骤之间所交换的数据只有标示为“自动机状态”的变量(此例中为变量state)。
自动机编程可以用显式的状态转换表来表示。以下的程序中的the_table数组即为状态转换表,其列表示三个不同的状态,其每一栏对应输入的字符(从左到右分别是空白、换行字符及其他字符)。对于每一种可能的状态及输入字符的组合,表中有其对应的新状态及一个决定是否否显示输入字符的旗标。在实务的项目中状态转换表可能更为复杂,例如可能包括所有可能条件组合下需调用的函数指针。
#include <stdio.h>
enum states { before = 0, inside = 1, after = 2 };
struct branch {
unsigned char new_state:2;
unsigned char should_putchar:1;
};
struct branch the_table[3][3] = {
/* ' ' '\n' others */
/* before */ { {before,0}, {before,1}, {inside,1} },
/* inside */ { {after, 0}, {before,1}, {inside,1} },
/* after */ { {after, 0}, {before,1}, {after, 0} }
};
void step(enum states *state, int c)
{
int idx2 = (c == ' ') ? 0 : (c == '\n') ? 1 : 2;
struct branch *b = & the_table[*state][idx2];
*state = (enum states)(b->new_state);
if(b->should_putchar) putchar(c);
}
int main(void)
{
int c;
enum states state = before;
while((c = getchar()) != EOF)
step(&state, c);
return 0;
}
若编程语言支持面向对象程序设计,就可以将自动机封装为一个对象,隐藏自动机实现的细节。一种称为“状态模式”的设计模式即包括了此作法。上述的程序可以改为为以下的面向对象程序,利用C++来实现:
#include <stdio.h>
class StateMachine {
enum states { before = 0, inside = 1, after = 2 } state;
struct branch {
enum states new_state:2;
int should_putchar:1;
};
static struct branch the_table[3][3];
public:
StateMachine() : state(before) {}
void FeedChar(int c) {
int idx2 = (c == ' ') ? 0 : (c == '\n') ? 1 : 2;
struct branch *b = & the_table[state][idx2];
state = b->new_state;
if(b->should_putchar) putchar(c);
}
};
struct StateMachine::branch StateMachine::the_table[3][3] = {
/* ' ' '\n' others */
/* before */ { {before,0}, {before,1}, {inside,1} },
/* inside */ { {after, 0}, {before,1}, {inside,1} },
/* after */ { {after, 0}, {before,1}, {after, 0} }
};
int main(void)
{
int c;
StateMachine machine;
while((c = getchar()) != EOF)
machine.FeedChar(c);
return 0;
}
4.参考:
http://zh.wikipedia.org/wiki/%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E6%9C%BAhttp://zh.wikipedia.org/wiki/%E5%9F%BA%E4%BA%8E%E8%87%AA%E5%8A%A8%E6%9C%BA%E7%BC%96%E7%A8%8B