java实现细胞自动机
如果您发现自己在设计上使用FSM的情况,则可能已经开始为实现相同接口的每个状态编写类。 一个好的设计可能是:
interface State { }
class State_1 implements State {}
class State_2 implements State {}
...
您可能有一个类可以管理这些状态以及它们之间的过渡,而另一个可以实现FSM的上下文(输入带状区域),另一个用于起始状态的接口,另一个用于结束状态的接口,依此类推。 许多类散布在许多文件中,使您无法快速跟踪它们。
还有另一种方法:使用枚举。
枚举本质上是类的列表,并且枚举的每个成员可能具有不同的实现。 假设我们要实现以下状态机:
初始化-('a')-> A
初始化-(else)->失败
A-('b')-> B A-('c')-> C A-('a')-> A A-(”)->结束 A-(其他)->失败 B-('c')-> C B-('b')-> B B-(”)->结束 B-(其他)->失败 C-('c')-> C C-(”)->结束 C-(其他)->失败
该FSM将验证以下正则表达式:^(a +)(b *)(c *)$。 我们可以将状态写成枚举状态的元素,如下所示:
interface State {public State next();
}
class Input {private String input;private int current;public Input(String input) {this.input = input;}char read() { return input.charAt(current++); }
}enum States implements State {Init {@Overridepublic State next(Input word) {switch(word.read()) {case 'a': return A;default: return Fail;}}},A {@Overridepublic State next(Input word) {switch(word.read()) {case 'a': return A;case 'b': return B;case 'c': return C;case '': return null;default: return Fail;}}},B {@Overridepublic State next(Input word) {switch(word.read()) {case 'b': return B;case 'c': return C;case '': return null;default: return Fail;}}},C {@Overridepublic State next(Input word) {switch(word.read()) {case 'c': return C;case '': return null;default: return Fail;}}},Fail {@Overridepublic State next(Input word) {return Fail;}};public abstract State next(Input word);
}
我们要做的是定义每个枚举中每个状态的转换。 每个过渡都会返回一个新状态,因此我们可以更有效地循环遍历它们:
State s;
Input in = new Input("aabbbc");
for(s = States.Init; s != null || s != States.Fail; s = s.next(in)) {}if(s == null) {System.out.printlin("Valid!");}
else {System.out.println("Failed");}
此时,我们要么验证了字符串,要么失败了。 这是一个简单而优雅的设计。
我们可以通过将最终状态与主要状态分开来进一步改善实现,以简化遍历的退出条件。 我们将定义另一个名为FinalState的接口,以及一个将包含所需退出状态的第二枚举(相应地更改States枚举):
interface FinalState extends State {}
enum FinalStates implements FinalState {Fail {@Overridepublic State next(Input word) {return Fail;}},Ok {@Overridepublic State next(Input word) {return End;}}
}
这样,遍历将有所简化:
for(s = States.Init; !(s instanceof FinalState); s = s.next(in)) {}
switch(s) {case Fail: System.out.printlin("Failed"); break;case Ok: System.out.println("Valid!"); break;default: System.out.println("Undefined"); break;
}
优点是(除了更简单的遍历之外),我们可以指定两个以上的最终状态。 在大多数情况下,FSM将有多个出口点,因此建议从长远来看最后一种改进。 您还可以将所有这些枚举和接口放在同一源文件中,并将整个逻辑放在一个位置,而不是浏览多个选项卡,以便了解流程的工作原理。
结论是,使用枚举可以更紧凑,更有意义地实现FSM。 您将所有逻辑都放在一个文件中,并且所有遍历都是轻松的。 您还将获得更加轻松的调试体验,因为已转换状态的名称将显示在调试器中(变量s将相应地更改其值,并带有新状态的名称),而不必弄清楚您刚刚上过什么课。 总而言之,我认为这是一个好技术。
参考:我们的JCG合作伙伴 Attila-Mihaly Balazs 在Java中实现自动机 在Transylvania JUG博客上。
翻译自: https://www.javacodegeeks.com/2012/03/automaton-implementation-in-java.html
java实现细胞自动机