从jsoup而来,文章见: https://github.com/code4craft/jsoup-learning/blob/master/blogs/jsoup4.md

状态机

Jsoup的词法分析和语法分析都用到了状态机。状态机可以理解为一个特殊的程序模型,例如经常跟我们打交道的正则表达式就是用状态机实现的。

它由状态(state)和转移(transition)两部分构成。根据状态转移的可能性,状态机又分为DFA(确定有限状态机)和NFA(非确定有限状态自动机)。这里拿一个最简单的正则表达式"a[b]*"作为例子,我们先把它映射到一个状态机DFA,大概是这样子:

状态机本身是一个编程模型,这里我们尝试用程序去实现它,那么最直接的方式大概是这样:

    public void process(StringReader reader) throws StringReader.EOFException {
char ch;
switch (state) {
case Init:
ch = reader.read();
if (ch == 'a') {
state = State.AfterA;
accum.append(ch);
}
break;
case AfterA:
...
break;
case AfterB:
...
break;
case Accept:
...
break;
}
}

这样写简单的状态机倒没有问题,但是复杂情况下就有点难受了。还有一种标准的状态机解法,先建立状态转移表,然后使用这个表建立状态机。这个方法的问题就是,只能做纯状态转移,无法在代码级别操作输入输出。

Jsoup里则使用了状态模式来实现状态机,初次看到时,确实让人眼前一亮。状态模式是设计模式的一种,它将状态和对应的行为绑定在一起。而在状态机的实现过程中,使用它来实现状态转移时的处理再合适不过了。

"a[b]*"的例子的状态模式实现如下,这里采用了与Jsoup相同的方式,用到了枚举来实现状态模式:

    public class StateModelABStateMachine implements ABStateMachine {

        State state;

        StringBuilder accum;

        enum State {
Init {
@Override
public void process(StateModelABStateMachine stateModelABStateMachine, StringReader reader) throws StringReader.EOFException {
char ch = reader.read();
if (ch == 'a') {
stateModelABStateMachine.state = AfterA;
stateModelABStateMachine.accum.append(ch);
}
}
},
Accept {
...
},
AfterA {
...
},
AfterB {
...
}; public void process(StateModelABStateMachine stateModelABStateMachine, StringReader reader) throws StringReader.EOFException {
}
} public void process(StringReader reader) throws StringReader.EOFException {
state.process(this, reader);
}
}

完整的实现程序如下:

StateModelABStateMachine.java:

package us.codecraft.learning.automata;

/**
* @author code4crafter@gmail.com
*/
public class StateModelABStateMachine implements ABStateMachine {
State state = State.Init;
StringBuilder accum = new StringBuilder(); enum State {
Init {
@Override
public void process(StateModelABStateMachine stateModelABStateMachine, StringReader reader) throws StringReader.EOFException {
char ch = reader.read();
if (ch == 'a') {
stateModelABStateMachine.state = AfterA;
stateModelABStateMachine.accum.append(ch);
}
}
}, Accept {
@Override
public void process(StateModelABStateMachine stateModelABStateMachine, StringReader reader) throws StringReader.EOFException {
System.out.println("find " + stateModelABStateMachine.accum.toString());
stateModelABStateMachine.accum = new StringBuilder();
stateModelABStateMachine.state = Init;
reader.unread();
}
}, AfterA {
@Override
public void process(StateModelABStateMachine stateModelABStateMachine, StringReader reader) throws StringReader.EOFException {
char ch = reader.read();
if (ch == 'b') {
stateModelABStateMachine.accum.append(ch);
stateModelABStateMachine.state = AfterB;
} else {
stateModelABStateMachine.state = Accept;
}
}
}, AfterB {
@Override
public void process(StateModelABStateMachine stateModelABStateMachine, StringReader reader) throws StringReader.EOFException {
char ch = reader.read();
if (ch == 'b') {
stateModelABStateMachine.accum.append(ch);
stateModelABStateMachine.state = AfterB;
} else {
stateModelABStateMachine.state = Accept;
}
}
}; public void process(StateModelABStateMachine stateModelABStateMachine, StringReader reader) throws StringReader.EOFException {
}
} @Override
public void process(StringReader reader) throws StringReader.EOFException {
state.process(this, reader);
} public static void main(String[] args) {
ABStateMachine abStateMachine = new StateModelABStateMachine();
String text = "abbbababbbaa";
StringReader reader = new StringReader(text);
try {
while (true) {
abStateMachine.process(reader);
}
} catch (StringReader.EOFException e) {
}
}
}

ABStateMachine.java:

package us.codecraft.learning.automata;

/**
* @author code4crafter@gmail.com
*/
public interface ABStateMachine {
void process(StringReader reader) throws StringReader.EOFException;
}

StringReader.java:

package us.codecraft.learning.automata;

/**
* @author code4crafter@gmail.com
*/
public class StringReader {
class EOFException extends Exception {} private String string;
private int index; public StringReader(String string) {
this.string = string;
} public char read() throws EOFException {
if (index < string.length() - 1) {
return string.charAt(index++);
} else {
throw new EOFException();
}
} public void unread() {
index--;
if (index < 0) {
index = 0;
}
}
}

一个相当好的状态机(DFA, 确定有限状态机)的编码实现,相当简洁漂亮的更多相关文章

  1. Stateless是一个基于C#创建状态机的简单库

    Stateless是一个基于C#创建状态机的简单库 .Net轻量状态机Stateless 很多业务系统开发中,不可避免的会出现状态变化,通常采用的情形可能是使用工作流去完成,但是对于简单场景下,用工作 ...

  2. 一个简洁漂亮的jQuery拖放排序插件DDSort

    拖放排序是WEB应用中常见的功能.虽然网上有很多别人已经造好的轮子,但是就我个人而言,没事就喜欢研究原理,自己造轮子,不管强大与否,简洁够用就是我的目标,再一个就是自己写的东西,应用起来得心应手,修改 ...

  3. halo的工作目录,有一个是在代码里配置的,硬编码了

    在HaloProperties.java中: /** * Work directory. */private String workDir = HaloConst.USER_HOME + " ...

  4. Java Secret: Using an enum to build a State machine(Java秘术:用枚举构建一个状态机)

    近期在读Hadoop#Yarn部分的源代码.读到状态机那一部分的时候,感到enmu的使用方法实在是太灵活了,在给并发编程网翻译一篇文章的时候,正好碰到一篇这种文章.就赶紧翻译下来,涨涨姿势. 原文链接 ...

  5. 趣说游戏AI开发:对状态机的褒扬和批判

    0x00 前言 因为临近年关工作繁忙,已经有一段时间没有更新博客了.到了元旦终于有时间来写点东西,既是积累也是分享.如题目所示,本文要来聊一聊在游戏开发中经常会涉及到的话题--游戏AI.设计游戏AI的 ...

  6. 08-FPGA状态机设计实例——小梅哥FPGA设计思想与验证方法视频教程配套文档

    芯航线--普利斯队长精心奉献   实验目的:1.学习状态机的相关概念 2.理解一段式.两段式以及三段式状态机的区别以及优缺点 实验平台:芯航线FPGA核心板 实验原理: 状态机全称是有限状态机(fin ...

  7. 试试用有限状态机的思路来定义javascript组件

    本文是一篇学习性的文章,学习利用有限状态机的思想来定义javascript组件的方法,欢迎阅读,后续计划会写几篇专门介绍自己利用有限状态机帮助自己编写组件的博客,证明这种思路对于编程实现的价值,目前正 ...

  8. Verilog学习笔记简单功能实现(三)...............同步有限状态机

    在Verilog中可以采用多种方法来描述有限状态机最常见的方法就是用always和case语句.如下图所示的状态转移图就表示了一个简单的有限状态机: 图中:图表示了一个四状态的状态机,输入为A和Res ...

  9. FPGA 状态机设计

    数字系统有两大类有限状态机(Finite State Machine,FSM):Moore状态机和Mealy状态机. Moore状态机 其最大特点是输出只由当前状态确定,与输入无关.Moore状态机的 ...

随机推荐

  1. .net类库中和数据库相关的

    System.Data.SqlTypes SqlDbType 枚举类型 Specifies SQL Server-specific data type of a field, property, fo ...

  2. Innodb物理存储结构系列2 行记录格式

    前一篇讨论了Innodb system,表空间,文件的关系及数据结构,这一篇记录下Innodb行记录的格式. 前提: 1. server层和innodb层都有自己对于record的记录格式,需要进行转 ...

  3. UVa (一道比较复杂的广搜) 816 Abbott’s Revenge

    题意: 给出一个迷宫,在迷宫的节点处,面向某个方向只能向给定的方向转弯.给出起点和终点输出迷宫的最短路径,这里指的是刚刚离开起点的时刻,所以即使起点和终点重合路径也非空. 分析: 用三个变量来表示状态 ...

  4. 一招解决OpenERP8.0安装旧版模块报错

    有喜欢尝鲜的网友开始玩8.0了,可是版本还没发布,社区的很多特别好的模块还没有升级到8,所以经常碰到模块无法安装的问题. No module name osv 网友提出将模块的 from osv im ...

  5. 如何在不同编程语言中获取现在的Unix时间戳(Unix timestamp)?

    Java time JavaScript Math.round(new Date().getTime()/1000) 之所以除以1000是因为getTime()返回数值的单位是毫秒 Microsoft ...

  6. hibernate一个注册小例子

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXUAAAJ2CAIAAAAv44WsAAAgAElEQVR4nO29a3QUVaL33fPhfHrW8y

  7. acdream 1412 2-3Trees (组合+DP)

    题意:2-3树的每个结点(除了叶子外)有2或3个孩子(分支),假设是一个满2-3树,那么给出叶子的数量,求这样的树有多少棵.(注:有2个孩子的结点视为相同,有3个孩子的结点视为相同,比如倒数第2层有4 ...

  8. php 系统命令执行函数

    (转载)作者:海底苍鹰地址:http://blog.51yip.com/php/1064.html 1,exec函数 <?php $test = "ls /tmp/test" ...

  9. 一:ZooKeeper简介

    一:背景                --->随着互联网技术的高速发展,企业对计算机系统的计算,存储能力要求越来越高,最简单的明证就是出现一些诸如:高并发,海量存储这样的词汇.在这样的背景下, ...

  10. The Network Adapter could not establish the connection解决办法

    用 oracle net manager 将监听改为IP地址,将服务命名也改为IP地址,然后数据库连接改为IP地址方式不要用localhost