自动机编程

历史

自动机编程的技术常用在以自动机原理为基础的算法中，例如形式语言分析。
约翰逊等在1968年发表的《Automatic generation of efficient lexical processors using finite state techniques》论文是早期提到自动机编程的论文。 Peter Naur在1963年的论文将自动机编程当成一种通用的软件技术。作者将此技术称为“图灵机的方法”，不过此论文是以自动机的状态及步骤为基础，没有提到图灵机。

示例

考虑一个C语言的程序，由标准输入流一行一行的读取数据，打印各一行的第一个英文单字。因此一开始需确认第一个英文单字之前是否有空白，若有，需读取所有空白后略过不打印，读取第一个英文单字然后打印，之后读取其他内容略过不打印，直到读到换行符号为止。任何情形下只要读到换行符号，就重新开始此算法，任何情形下只要读到文件结束（end-of-file）的符号，就结束程序。

传统C语言的程序

以下是传统指令式编程的C语言程序：
#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;}

显式的状态转换表

自动机编程可以用显式的状态转换表来表示。以下的程序中的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;}

自动化技术及事件

在自动化领域中，步骤之间的切换是依照机器本身的输入数据，在本例中为读到的输入字符，在实务上可能是位置、速度、温度等机器的关键数据。
自动化领域有些设计方式类似图形用户界面的程序设计，机器状态的改变可以视为由事件而造成，由于事件使机器由一个状态变为下一个状态，直到到达最后的状态为止。可能出现状态的组合可以产生许多的事件，因此可以定义较复杂的制造周期，其产生的制造周期一般会比线性循序流程复杂许多。一般常常会有一些同时运行的平行路径，以及依不同事件决定运行方式的路径，如下图：

   s:狀態   c:條件
   
   s1
   |
   |-c2
   |
   s2
   |
   ----------
   |        |
   |-c31    |-c32
   |        |
  s31       s32
   |        |
   |-c41    |-c42
   |        |
   ----------
   |
   s4

面向对象程序

若编程语言支持面向对象程序设计，就可以将自动机封装为一个对象，隐藏自动机实现的细节。一种称为“状态模式”的设计模式即包括了此作法。上述的程序可以改为为以下的面向对象程序，利用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;}
注：为了减少和此主题不直接相关的修改，此处的输入输出函数使用C语言的标准库，另外，其中的三元运算符?:也可以用if-else来实现。

应用

自动机编程常用在词法分析及语法分析器中。
此外，用自动机的方式处理问题（将运行的程序分为自动机的步骤，以及各步骤间只通过显式的状态传递信息）是事件驱动程序设计中必要的一部分，否则就要使用平行程序或是多线程的作法。
状态及状态机的表示法常用在形式规格的领域。例如以统一塑模语言为基础的软件架构开发，会使用状态图表示程序的行为，许多通信协议也利用显式的状态来加以定义，例如RFC 793。
状态机的思维也可以用来描述一些编程语言的语义，例如运行一个Refal语言的程序就可以描述为在抽象Refal机器上运行一连串的步骤，机器的状态称为view（任意的Refal表示式，其中没有变量）。
Scheme编程语言不是一个和状态机有关的编程语言（Scheme为递归式的），但其中的延续性（Continuation）需要以自动机的步骤及状态的方式来思考。若要使call/cc的机能有效，需要可以记录整个运行程序的状态，只有在所有状态都是显式，不存在隐式状态的情形下才可能达到。此处的“记录完整状态”即为延续性，可以视为一个较复杂自动机的状态，自动机的步骤是由以前的延续性数据推断下一个的延续性数据，而所运行的程序就是这些步骤的循环。
亚历山大·奥隆格罗（Alexander Ollongren）在其著作中解释了一种称为“维也纳方法”（Vienna method）的编程语言语义描述，完全以形式自动机为基础。
UCSB的STAT（状态转移分析技术）系统是一个使用自动机编程的示例，此系统还包括一种称为“STATL”的嵌入式语言，是完全自动机导向的语言。

和面向对象程序设计的关系

在面向对象程序设计的理论中，对象有其内部的状态，而且可以接收消息、回应消息，发送消息给其他对象，且依消息调整其内部的状态。实际上，调用一个对象的方法也就是发送消息给此一对象。
因此，面向对象程序设计的对象也可以视为是自动机（或是自动机的模型），其状态是内部属性的组合，而对象的一个或多个方法可视为自动机的步骤。这些视为自动机步骤的方法不能直接或间接的互相调用（或调用本身），否则此对象就不能视为以自动机编程的方式来设计。
当用面向对象程序设计来实现自动机编程时，也可以用类别来实现自动机模型，其中状态为其私有成员，而步骤是对象的一个公开方法，是除了构造函数及析构函数外，唯一可以变更对象内容的公开方法。对象的其他公开方法可以查询状态，但不能变更其状态。对象的其他方法（例如不同状态的进程）会是对象的私有方法，无法由外界程序来调用。