﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-woaidongmao-随笔分类-自动机 &amp; 形式语言</title><link>http://www.cppblog.com/woaidongmao/category/9130.html</link><description>文章均收录自他人博客，但不喜标题前加-[转贴]，因其丑陋，见谅！~</description><language>zh-cn</language><lastBuildDate>Fri, 26 Dec 2008 00:32:26 GMT</lastBuildDate><pubDate>Fri, 26 Dec 2008 00:32:26 GMT</pubDate><ttl>60</ttl><item><title>Ragel 状态机简介</title><link>http://www.cppblog.com/woaidongmao/archive/2008/12/24/70245.html</link><dc:creator>肥仔</dc:creator><author>肥仔</author><pubDate>Wed, 24 Dec 2008 07:18:00 GMT</pubDate><guid>http://www.cppblog.com/woaidongmao/archive/2008/12/24/70245.html</guid><wfw:comment>http://www.cppblog.com/woaidongmao/comments/70245.html</wfw:comment><comments>http://www.cppblog.com/woaidongmao/archive/2008/12/24/70245.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/woaidongmao/comments/commentRss/70245.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/woaidongmao/services/trackbacks/70245.html</trackback:ping><description><![CDATA[<p class="MsoNormal" style="margin-bottom: 12pt"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma">从今天开始我把我前一段时间用到的状态机工具<span lang="EN-US">Ragel</span>的使用方法做一些总结<span lang="EN-US">,</span>希望大家斧正<span lang="EN-US">!<br><br><br>(</span>如果大家对状态机概念有模糊的话<span lang="EN-US">,</span>请参考<span lang="EN-US">&lt;&lt;</span>编译原理<span lang="EN-US">&gt;&gt;</span>一书<span lang="EN-US">,</span>基本上有详尽的介绍<span lang="EN-US">)<br><br></span>好闲言少叙<span lang="EN-US">,</span>言归正传<span lang="EN-US"><br><br>Ragel</span>可以从正规表达式生成可执行有限状态机<span lang="EN-US">,</span>它可以生成<span lang="EN-US">C,C++,Object-C,D,Java</span>和<span lang="EN-US">Ruby</span>可执行代码<span lang="EN-US"><br><br></span>官方网站<span lang="EN-US">:<a href="http://www.cs.queensu.ca/home/thurston/ragel/" target="_blank"><span style="color: windowtext">http://www.cs.queensu.ca/home/thurston/ragel/</span></a><br><br></span>第一回<span lang="EN-US"><br><br>Ragel</span>是一个可以生成协议处理代码的工具<span lang="EN-US">.</span>　<span lang="EN-US"><br><br></span>先举个例子，简简单单的几行代码<span lang="EN-US">,</span>实现的功能为将一个数字字符串转换成整数：<span lang="EN-US"><?xml:namespace prefix = o /><o:p></o:p></span></span></p> <p class="MsoNormal" style="background: #f3f8d7; text-align: right" align="right"><b><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: tahoma"><a href="http://www.unix-center.net/bbs/viewthread.php?tid=3511######"><span style="mso-bidi-font-family: arial">[Copy to clipboard]</span></a> <a href="http://www.unix-center.net/bbs/viewthread.php?tid=3511######"><span style="mso-bidi-font-family: arial">[ <span id="code0_symbol">-</span> ]</span></a><o:p></o:p></span></b></p> <p class="MsoNormal" style="background: #f3f8d7; text-align: left" align="left"><b><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: tahoma">CODE:<o:p></o:p></span></b></p> <p class="MsoNormal" style="background: #fdfff2"><span lang="EN-US" style="font-size: 12pt; color: #333333; font-family: 宋体; mso-bidi-font-family: tahoma">int atoi( char *str )<br>{<br>char *p = str;<br>int cs, val = 0;<br>bool neg = false;<br><br>%%{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Ragel </span><span style="font-size: 12pt; color: #333333; font-family: 宋体; mso-bidi-font-family: tahoma">的关键字<span lang="EN-US">,</span>用于声明状态机代码段的开始<span lang="EN-US"><br>&nbsp; action see_neg {<br>&nbsp;&nbsp; neg = true;<br>&nbsp; }<br><br>&nbsp; action add_digit {<br>&nbsp;&nbsp; val = val * 10 + (fc - '0');<br>&nbsp; }<br><br>&nbsp; main :=<br>&nbsp;&nbsp; ( '-'@see_neg | '+' )? ( digit @add_digit )+<br>&nbsp;&nbsp; '\n' @{ fbreak; };<br><br>&nbsp; # Initialize and execute.<br>&nbsp; write init;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>状态机关键字<span lang="EN-US">,</span>这个会再接下来的内容中介绍<span lang="EN-US"><br>&nbsp; write exec noend;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>同上<span lang="EN-US"><br>}%%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>状态机代码段结束标记<span lang="EN-US"><br><br>if ( neg )<br>&nbsp; val = -1 * val;<br><br>if ( cs &lt; atoi_first_final )<br>&nbsp; cerr &lt;&lt; "atoi: there was an error" &lt;&lt; endl;<br><br>return val;<br>};<o:p></o:p></span></span></p> <p class="MsoNormal" style="margin-bottom: 12pt"><span lang="EN-US" style="font-size: 12pt; color: #333333; font-family: 宋体; mso-bidi-font-family: tahoma"><br></span><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma">比<span lang="EN-US">c</span>里面那<span lang="EN-US">500</span>多行实现的<span lang="EN-US">atoi</span>函数更加高效<span lang="EN-US"><br><br></span>上面这段代码，生成的<span lang="EN-US">C</span>语言代码如下<span lang="EN-US">:<o:p></o:p></span></span></p> <p class="MsoNormal" style="background: #f3f8d7; text-align: right" align="right"><b><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: tahoma"><a href="http://www.unix-center.net/bbs/viewthread.php?tid=3511######"><span style="mso-bidi-font-family: arial">[Copy to clipboard]</span></a> <a href="http://www.unix-center.net/bbs/viewthread.php?tid=3511######"><span style="mso-bidi-font-family: arial">[ <span id="code1_symbol">-</span> ]</span></a><o:p></o:p></span></b></p> <p class="MsoNormal" style="background: #f3f8d7; text-align: left" align="left"><b><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: tahoma">CODE:<o:p></o:p></span></b></p> <p class="MsoNormal" style="background: #fdfff2"><span lang="EN-US" style="font-size: 12pt; color: #333333; font-family: 宋体; mso-bidi-font-family: tahoma">int atoi( char *str )<br>{<br>char *p = str;<br>int cs, val = 0;<br>bool neg = false;<br><br>#line 27 "atoi.c"<br>{<br>cs = atoi_start;<br>}<br><br>#line 31 "atoi.c"<br>{<br>switch ( cs )<br>{<br>case 1:<br>switch( (*p) ) {<br>&nbsp; case 43: goto st2;<br>&nbsp; case 45: goto tr2;<br>}<br>if ( 48 &lt;= (*p) &amp;&amp; (*p) &lt;= 57 )<br>&nbsp; goto tr3;<br>goto st0;<br>st0:<br>goto _out0;<br>tr2:<br>#line 23 "atoi.rl"<br>{<br>&nbsp;&nbsp; neg = true;<br>&nbsp; }<br>goto st2;<br>st2:<br>p += 1;<br>case 2:<br>#line 52 "atoi.c"<br>if ( 48 &lt;= (*p) &amp;&amp; (*p) &lt;= 57 )<br>&nbsp; goto tr3;<br>goto st0;<br>tr3:<br>#line 27 "atoi.rl"<br>{<br>&nbsp;&nbsp; val = val * 10 + ((*p) - '0');<br>&nbsp; }<br>goto st3;<br>st3:<br>p += 1;<br>case 3:<br>#line 63 "atoi.c"<br>if ( (*p) == 10 )<br>&nbsp; goto tr4;<br>if ( 48 &lt;= (*p) &amp;&amp; (*p) &lt;= 57 )<br>&nbsp; goto tr3;<br>goto st0;<br>tr4:<br>#line 33 "atoi.rl"<br>{ goto _out4; }<br>goto st4;<br>st4:<br>p += 1;<br>case 4:<br>#line 74 "atoi.c"<br>goto st0;<br>}<br>_out0: cs = 0; goto _out;<br>_out4: cs = 4; goto _out;<br><br>_out: {}<br>}<br>#line 38 "atoi.rl"<br><br><br>if ( neg )<br>&nbsp; val = -1 * val;<br><br>if ( cs &lt; atoi_first_final )<br>&nbsp; cerr &lt;&lt; "atoi: there was an error" &lt;&lt; endl;<br><br>return val;<br>};<o:p></o:p></span></p> <p class="MsoNormal"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma"><br></span><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma">对应的状态图如下图所示<span lang="EN-US">:<span style="color: #333333"><br><br><a href="http://www.cppblog.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/Ragel_D708/clip_image001_2.gif"><img onmousewheel="return imgzoom(this);" style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="108" alt="clip_image001" src="http://www.cppblog.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/Ragel_D708/clip_image001_thumb.gif" width="606" border="0" v:shapes="_x0000_i1025"></a><o:p></o:p></span></span></span></p> <p class="MsoNormal"><span lang="EN-US" style="font-size: 12pt; color: #333333; font-family: 宋体; mso-bidi-font-family: tahoma"><o:p>&nbsp;</o:p></span></p> <table class="MsoNormalTable" style="background: white; width: 100%; border-collapse: collapse; mso-padding-alt: 3.0pt 3.0pt 3.0pt 3.0pt" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes"> <td style="border-right: medium none; padding-right: 7.5pt; border-top: #bbe9ff 1pt solid; padding-left: 7.5pt; padding-bottom: 1.5pt; border-left: medium none; padding-top: 7.5pt; border-bottom: medium none; mso-border-top-alt: solid #bbe9ff .75pt" valign="top"> <p class="MsoNormal" style="margin-bottom: 12pt"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma">正则表达式广泛应用于解析器中。它们通常被用来作为<span lang="EN-US">“</span>黑盒<span lang="EN-US">”</span>与程序逻辑联系在一起。对正则表达式引擎在执行某些解析工作之后，调用用户自定义行为。加入新的自定义行为，需要重新定义原来的格局，然后粘贴到程序逻辑中。自定义行为越多，正规表达式的优势越小。<span lang="EN-US"><br><br>Ragel</span>是一个可以根据用户定义的正则表达式或是由正则表达式生成的状态图来生成健壮的，无依赖的可执行代码，包括<span lang="EN-US">C</span>，<span lang="EN-US">C++</span>，<span lang="EN-US">Object-C, Java, Ruby </span>等等<span lang="EN-US">. </span>可以灵活控制已经生成状态机的变动，利用已经嵌入自定义行为的模式重构扫描器<span lang="EN-US"><br><br>Ragel</span>基于任何正规语言能被转化为有限状态自动机的原理<span lang="EN-US"><br><br></span>基本的使用步骤如下<span lang="EN-US">:<br><br></span>首先<span lang="EN-US">,</span>根据你的业务流程逻辑与需要<span lang="EN-US">,</span>按照<span lang="EN-US">ragel</span>提供的语法与关键字编写<span lang="EN-US">.rl</span>文件<span lang="EN-US"><br><br>1</span>．<span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>编写<span lang="EN-US">.rl</span>文件<span lang="EN-US">, </span>如下所示：<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="background: #f3f8d7; text-align: right" align="right"><b><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: tahoma"><a href="http://www.unix-center.net/bbs/viewthread.php?tid=3597######"><span style="mso-bidi-font-family: arial">[Copy to clipboard]</span></a> <a href="http://www.unix-center.net/bbs/viewthread.php?tid=3597######"><span style="mso-bidi-font-family: arial">[ <span id="code0_symbol">-</span> ]</span></a><o:p></o:p></span></b></p> <p class="MsoNormal" style="background: #f3f8d7; text-align: left" align="left"><b><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: tahoma">CODE:<o:p></o:p></span></b></p> <p class="MsoNormal" style="background: #fdfff2"><span lang="EN-US" style="font-size: 12pt; color: #333333; font-family: 宋体; mso-bidi-font-family: tahoma">/*<br>* to parse a string started with “table” or “div”<br>*/<br><br>#include &lt;stdlib.h&gt;<br>#include &lt;string.h&gt;<br>#include &lt;stdio.h&gt;<br><br>%%{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #</span><span style="font-size: 12pt; color: #333333; font-family: 宋体; mso-bidi-font-family: tahoma">状态机的名字<span lang="EN-US">(</span>必须有一个名字而且必须符合命名规则<span lang="EN-US">,</span>和变通的变量声明一样<span lang="EN-US">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; machine par_str;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; write data;<br>}%%<br>//</span>函数声明<span lang="EN-US">,</span>用于在用户自定义行为中调用<span lang="EN-US">,</span>用到了参数的传递<span lang="EN-US">,</span>此函数也可以是类成员函数<span lang="EN-US"><br>void printtable(int len)<br>{<br>&nbsp;&nbsp; printf("there is a table,length is:%d\n",len);<br>}<br><br>//</span>另外一个函数<span lang="EN-US">,</span>功能同上<span lang="EN-US">,</span>只是参数传递用的是引用传递<span lang="EN-US"><br>void printdiv(char *p)<br>{<br>&nbsp; printf("%s\n",(*p));<br>}<br>//</span>主处理函数<span lang="EN-US"><br>void par_str( char *str,int len )<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *p = str, *pe = str + strlen( str );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int cs;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>状态机关键字<span lang="EN-US">,</span>用于说明当明状态<span lang="EN-US">,</span>以整型值标识<span lang="EN-US">(current status</span>的缩写<span lang="EN-US">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>状态机自定义行为块<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %%{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #</span>调用自定义函数<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; action see_table {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printtable(len);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #invoke prindiv function to show the table information, as same as above<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; action see_div { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printdiv(p);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #</span>正则表达式声明<span lang="EN-US">,</span>用于在状态机初始化时<span lang="EN-US">,</span>按照此规则匹配目标<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main := <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ([t][a][b][l][e]@see_table) ([d][i][v]@see_div)+'\n';<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # </span>初始化<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; write init;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; write exec;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }%%<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( cs &lt; par_str_first_final )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf( stderr, "par_str: there was an error\n" ); <br>};<br><br>#define BUFSIZE 1024<br>//</span>主函数<span lang="EN-US">,</span>用于测试生成的状态机<span lang="EN-US"><br>int main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char buf[BUFSIZE];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while ( fgets( buf, sizeof(buf), stdin ) != 0 ) <br>{<br>par_str(buf,10);<br>}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>}<o:p></o:p></span></span></p> <p class="MsoNormal" style="margin-bottom: 12pt"><span lang="EN-US" style="font-size: 12pt; color: #333333; font-family: 宋体; mso-bidi-font-family: tahoma"><br></span><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma">接下来<span lang="EN-US">,</span>用<span lang="EN-US">ragel</span>命令生成目的语言文件<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="background: #f3f8d7; text-align: left" align="left"><b><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: tahoma">CODE:<o:p></o:p></span></b></p> <p class="MsoNormal" style="background: #fdfff2"><span lang="EN-US" style="font-size: 12pt; color: #333333; font-family: 宋体; mso-bidi-font-family: tahoma">ragel -o test.cpp test.rl<o:p></o:p></span></p> <p class="MsoNormal" style="margin-bottom: 12pt"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma"><br></span><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma">用代码生成工具<span lang="EN-US">,</span>直接生成可执行代码<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="background: #f3f8d7; text-align: left" align="left"><b><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: tahoma">CODE:<o:p></o:p></span></b></p> <p class="MsoNormal" style="background: #fdfff2"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma">rlcodegen -o hello.cpp test.cpp<o:p></o:p></span></p> <p class="MsoNormal"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma"><br></span><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma">最后编写对此代码的<span lang="EN-US">Makefile,make........<o:p></o:p></span></span></p> <p class="MsoNormal"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: tahoma"><br style="mso-special-character: line-break" clear="all"><span style="color: #333333"><o:p></o:p></span></span></p></td></tr> <tr style="mso-yfti-irow: 1; mso-yfti-lastrow: yes"> <td style="padding-right: 7.5pt; padding-left: 7.5pt; padding-bottom: 1.5pt; padding-top: 1.5pt" valign="bottom"> <p class="MsoNormal"><span lang="EN-US" style="font-size: 12pt; color: #333333; font-family: 宋体; mso-bidi-font-family: tahoma"><o:p>&nbsp;</o:p></span></p></td></tr></tbody></table> <p class="MsoNormal"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: arial"><o:p>&nbsp;</o:p></span></p><img src ="http://www.cppblog.com/woaidongmao/aggbug/70245.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/woaidongmao/" target="_blank">肥仔</a> 2008-12-24 15:18 <a href="http://www.cppblog.com/woaidongmao/archive/2008/12/24/70245.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>状态机的应用实例----电子表</title><link>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69416.html</link><dc:creator>肥仔</dc:creator><author>肥仔</author><pubDate>Sun, 14 Dec 2008 11:33:00 GMT</pubDate><guid>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69416.html</guid><wfw:comment>http://www.cppblog.com/woaidongmao/comments/69416.html</wfw:comment><comments>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69416.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/woaidongmao/comments/commentRss/69416.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/woaidongmao/services/trackbacks/69416.html</trackback:ping><description><![CDATA[<p></p> <p class="MsoNormal" style="line-height: 150%"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial">&nbsp; </span><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial">看看小时候玩的<span lang="EN-US">5</span>块钱那种最简单的电子表。只有<span lang="EN-US">2</span>个按钮就能操作(暂且称为按钮<span lang="EN-US">A</span>和按钮<span lang="EN-US">B)。<br>&nbsp; </span>现给出一个完整的功能文字描述：<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在显示时间时按<span lang="EN-US">A</span>，屏幕显示变成日期<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在显示日期时按<span lang="EN-US">A</span>，屏幕显示变成秒钟<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在显示秒钟时按<span lang="EN-US">A</span>，屏幕显示变成时间<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在显示秒钟时按<span lang="EN-US">B</span>，秒钟归<span lang="EN-US">0<br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在显示时间时按<span lang="EN-US">B</span>，屏幕 时间、日期交替显示。<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在时间、日期交替显示时按<span lang="EN-US">B</span>，屏幕<span lang="EN-US">“</span>时<span lang="EN-US">”</span>闪烁<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在<span lang="EN-US">“</span>时<span lang="EN-US">”</span>闪烁时按<span lang="EN-US">B</span>，屏幕<span lang="EN-US">“</span>时<span lang="EN-US">”</span>加<span lang="EN-US">1</span>，超过<span lang="EN-US">23</span>回<span lang="EN-US">0<br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在<span lang="EN-US">“</span>时<span lang="EN-US">”</span>闪烁时按<span lang="EN-US">A</span>，屏幕<span lang="EN-US">“</span>分<span lang="EN-US">”</span>闪烁<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在<span lang="EN-US">“</span>分<span lang="EN-US">”</span>闪烁时按<span lang="EN-US">B</span>，屏幕<span lang="EN-US">“</span>分<span lang="EN-US">”</span>加<span lang="EN-US">1</span>，超过<span lang="EN-US">59</span>回<span lang="EN-US">0<br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在<span lang="EN-US">“</span>分<span lang="EN-US">”</span>闪烁时按<span lang="EN-US">A</span>，屏幕<span lang="EN-US">“</span>月<span lang="EN-US">”</span>闪烁<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在<span lang="EN-US">“</span>月<span lang="EN-US">”</span>闪烁时按<span lang="EN-US">B</span>，屏幕<span lang="EN-US">“</span>月<span lang="EN-US">”</span>加<span lang="EN-US">1</span>，超过<span lang="EN-US">12</span>回<span lang="EN-US">0<br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在<span lang="EN-US">“</span>月<span lang="EN-US">”</span>闪烁时按<span lang="EN-US">A</span>，屏幕<span lang="EN-US">“</span>日<span lang="EN-US">”</span>闪烁<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在<span lang="EN-US">“</span>日<span lang="EN-US">”</span>闪烁时按<span lang="EN-US">B</span>，屏幕<span lang="EN-US">“</span>日<span lang="EN-US">”</span>加<span lang="EN-US">1</span>，超过<span lang="EN-US">31</span>回<span lang="EN-US">0<br>&nbsp;&nbsp;&nbsp;&nbsp; </span>在<span lang="EN-US">“</span>日<span lang="EN-US">”</span>闪烁时按<span lang="EN-US">A</span>，屏幕回到时间显示<span lang="EN-US"><br><br>&nbsp;&nbsp;&nbsp; </span>如果按照新手的思路，尝试去画流程图，很快就会陷入一头雾水：你会发现实现这个功能的程序根本就没有<span lang="EN-US">“</span>确定的流程<span lang="EN-US">”</span>。因为程序实际流程是根据人的操作而变化的。程序运行到什么地方，完全取决于两个键的次序，有无数种次序组合，根本不可能画出流程图来。<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; </span>但是我们会发现，这个电子表功能的<span lang="EN-US">“</span>语言描述<span lang="EN-US">”</span>在语法上似乎有某种规律，就是：<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; </span>当系统处于某状态（<span lang="EN-US">S1</span>）时，如果发生了什么事情<span lang="EN-US">(E)</span>，就执行某功能<span lang="EN-US">(F)</span>，然后系统变成新状态（<span lang="EN-US">S2</span>）<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; </span>只要能用上面这句话描述的系统，都可以用一种状态跳转机制很方便的实现<span lang="EN-US"><br></span>，并且一句话其实就是一个<span lang="EN-US">if(...)</span>，无论有多少多复杂的功能，只要能用上面这句话描述，都可以通过状态机编程实现。<span lang="EN-US">&nbsp;&nbsp; <br>&nbsp;&nbsp; <br></span>我们将它们抽象。整个系统中有<span lang="EN-US">2</span>个事件分别是：<span lang="EN-US">A</span>按下，<span lang="EN-US">B</span>按下<span lang="EN-US"><br><br>&nbsp;&nbsp;&nbsp; A</span>按下<span lang="EN-US">(</span>可以是中断<span lang="EN-US">)</span>时执行：<span lang="EN-US"><br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; if(Status==TIME)&nbsp; //</span>当显示时间时按下<span lang="EN-US">A</span>键<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status=DATE&nbsp;&nbsp;&nbsp; //</span>变成显示日期<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; if(Status==DATE)&nbsp; //</span>当显示日期时按下<span lang="EN-US">A</span>键<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status=SEC&nbsp;&nbsp;&nbsp;&nbsp; //</span>变成显示秒钟<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; if(Status==SEC)&nbsp; //</span>当显示秒钟时按下<span lang="EN-US">A</span>键<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status=TIME&nbsp;&nbsp;&nbsp;&nbsp; //</span>变成显示时间<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; if(Status==SET_HOUR)&nbsp; //</span>当设置<span lang="EN-US">“</span>小时<span lang="EN-US">”</span>时按下<span lang="EN-US">A</span>键<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status=SET_MINUT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>变成设置<span lang="EN-US">“</span>分钟<span lang="EN-US">”<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; if(Status==SET_MINUT)&nbsp; //</span>当设置<span lang="EN-US">“</span>分钟<span lang="EN-US">”</span>时按下<span lang="EN-US">A</span>键<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status=SET_MONTH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>变成设置<span lang="EN-US">“</span>月<span lang="EN-US">”<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; .....<br>&nbsp;&nbsp;&nbsp;&nbsp; .....<br>}<br>&nbsp; <br><br>&nbsp;&nbsp;&nbsp; B</span>按下<span lang="EN-US">(</span>可以是中断<span lang="EN-US">)</span>时执行：<span lang="EN-US"><br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(Status==SEC)&nbsp; //</span>当显示秒钟时按下<span lang="EN-US">B</span>键<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Secound=0&nbsp;&nbsp;&nbsp;&nbsp; //</span>秒归<span lang="EN-US">0<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; if(Status==TIME)&nbsp; //</span>当显示时间时按下<span lang="EN-US">B</span>键<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status=TIMEDATE&nbsp;&nbsp;&nbsp; //</span>变成时间日期交替显示<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp;&nbsp; if(Status==TIMEDATE)&nbsp; //</span>当日期交替显示时按下<span lang="EN-US">B</span>键<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status=SET_HOUR&nbsp;&nbsp;&nbsp; //</span>变成设置<span lang="EN-US">“</span>时<span lang="EN-US">”</span>（时闪烁）<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; if(Status==SET_HOUR)&nbsp; //</span>当设置<span lang="EN-US">“</span>时<span lang="EN-US">”</span>时按下<span lang="EN-US">B</span>键<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status=Hour++&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>时加<span lang="EN-US">1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(Hour&gt;23) Hour="0";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp;&nbsp; .....<br>&nbsp;&nbsp;&nbsp;&nbsp; .....&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>}<br><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>和语言描述完全一致，很快就能写完程序。这就是最简单的状态机思想。<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>当然，上述一大堆<span lang="EN-US">if</span>可以用<span lang="EN-US">switch case</span>来实现<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp; </span>实际中，大量的并发过程都可以表述为状态跳转关系，从而将<span lang="EN-US">CPU</span>从过程中解放出来，只需处理状态关系，因为真正需要<span lang="EN-US">CPU</span>的是状态变化的时刻，而不是过程中大量的等待，这样大量的并发过程都可以并行处理。<span lang="EN-US"><?xml:namespace prefix = o /><o:p></o:p></span></span></p><img src ="http://www.cppblog.com/woaidongmao/aggbug/69416.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/woaidongmao/" target="_blank">肥仔</a> 2008-12-14 19:33 <a href="http://www.cppblog.com/woaidongmao/archive/2008/12/14/69416.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是有限状态机FSM</title><link>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69415.html</link><dc:creator>肥仔</dc:creator><author>肥仔</author><pubDate>Sun, 14 Dec 2008 10:54:00 GMT</pubDate><guid>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69415.html</guid><wfw:comment>http://www.cppblog.com/woaidongmao/comments/69415.html</wfw:comment><comments>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69415.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/woaidongmao/comments/commentRss/69415.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/woaidongmao/services/trackbacks/69415.html</trackback:ping><description><![CDATA[<p class="MsoNormal" style="line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><b style="mso-bidi-font-weight: normal"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">简介<span lang="EN-US"><?xml:namespace prefix = o /><o:p></o:p></span></span></b></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">有限状态机（以下用<span lang="EN-US">FSM</span>指代）是一种算法思想，简单而言，有限状态机由一组状态、一个初始状态、输入和根据输入及现有状态转换为下一个状态的转换函数组成。在<span lang="EN-US">Gof</span>的<span lang="EN-US">23</span>种设计模式里的<span lang="EN-US">state</span>模式是一种面向对象的状态机思想，可以适应非常复杂的状态管理。<span lang="EN-US"> <br></span>现 在，<span lang="EN-US">FSM</span>被普遍用于搜索引擎的分词、编译器实现和我们普遍关注的游戏开发中。游戏开发中，通常用<span lang="EN-US">FSM</span>实现<span lang="EN-US">NPC</span>控制，如当<span lang="EN-US">NPC</span>受到攻击时根据健康、力量等选择逃跑还是反攻的行为，一般是用<span lang="EN-US">FSM</span>实现的。<span lang="EN-US">FSM</span>的实现方法有很多种，不能简单地说孰优孰劣，但现代开发中，一般都比较推荐面向对象的实现方式：因为可重用性和健壮性更高，而且当需求变更的时候，也有很好的适应性。<span lang="EN-US"> <br></span><b style="mso-bidi-font-weight: normal">实践 </b><span lang="EN-US"><br></span>理 论从实践中来，也要回到实践中去。我们现在通过实例来探索一下<span lang="EN-US">FSM</span>的实现吧。首先假设有这样一个世界（<span lang="EN-US">World</span>），世界里只有一台永不缺乏动力的汽车 （<span lang="EN-US">Car</span>），汽车是次世代的，没有油门方向盘之类的落后设备，只有两个互斥的按钮<span lang="EN-US">——</span>停止（<span lang="EN-US">Stop</span>）和行进（<span lang="EN-US">Run</span>），随着时间的流逝，汽车根据驾驶员的操作走走停停。下面的代码可以实现这种功能：<span lang="EN-US"> <br><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>while True: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; key = get_key() # </span>按下什么键<span lang="EN-US"> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if key == "stop": <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stop(car) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elif key == "run": <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; go(car) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; keep(car) # </span>保持原态<span lang="EN-US">&nbsp;&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">完 成了功能而且直观、简洁的程序员万岁！但这时候客户（策划或者玩家）觉得走走停停太没意思了，他们想要掉头、左转和右转的功能，我们就要在<span lang="EN-US">while</span>循环 里增加更多的<span lang="EN-US">if...else</span>分支；他们想要更多的车，我们就要要在每一个分枝里增加循环；他们不仅仅想要<span lang="EN-US">Car</span>了，他们还要要玩<span lang="EN-US">Truck</span>，这时我们 就需要在分枝的循环里判断当前的车是否支持这个操作（如<span lang="EN-US">Truck</span>的装卸货物<span lang="EN-US">Car</span>就不支持）；他们<span lang="EN-US">…… <br></span>这个<span lang="EN-US">while</span>循环终于无限地庞大起来，我们认识到这样的设计的确是有点问题的，所以我们尝试用另一种方法去实现<span lang="EN-US">FSM</span>。首先我们来实现汽车（<span lang="EN-US">Car</span>）：<span lang="EN-US"> <br><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>class Car(object): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def stop(self): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print "Stop!!!" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def go(self): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print "Goooooo!!!"&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="margin-left: 14.9pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan; mso-para-margin-left: 1.42gd" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">只有两个方法<span lang="EN-US">stop</span>和<span lang="EN-US">go</span>，分别执行<span lang="EN-US">Stop</span>和<span lang="EN-US">Run</span>两个按钮功能。接下来我们编写两个状态管理的类，用以处理当按钮被按下、弹起和保持时需要工作的流程：<span lang="EN-US"> <br>class stop_fsm(base_fsm): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def enter_state(self, obj): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print "Car%s enter stop state!"%(id(obj)) <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def exec_state(self, obj): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print "Car%s in stop state!"%(id(obj)) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj.stop() <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def exit_state(self, obj): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print "Car%s exit stop state!"%(id(obj))&nbsp; <br>class run_fsm(base_fsm): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def enter_state(self, obj): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print "Car%s enter run state!"%(id(obj)) <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def exec_state(self, obj): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print "Car%s in run state!"%(id(obj)) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj.go() <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def exit_state(self, obj): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print "Car%s exit run state!"%(id(obj))&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">stop_fsm</span><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">和<span lang="EN-US">run_fsm</span>都继承自<span lang="EN-US">base_fsm</span>，<span lang="EN-US">base_fsm</span>是一个纯虚的接口类：<span lang="EN-US"> <br>class base_fsm(object): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def enter_state(self, obj): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise NotImplementedError() <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def exec_state(self, obj): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise NotImplementedError() <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def exit_state(self, obj): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise NotImplementedError()&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">enter_state </span><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">在<span lang="EN-US">obj</span>进入某状态的时候调用<span lang="EN-US">——</span>通常用来做一些初始化工作；<span lang="EN-US">exit_state</span>也离开某状态的时候调用<span lang="EN-US">——</span>通常做一些清理工作；而<span lang="EN-US"> exec_state</span>则在每一帧的时候都会被调用，通常做一些必要的工作，如检测自己的消息队列并处理消息等。在<span lang="EN-US">stop_fsm</span>和<span lang="EN-US">run_fsm</span>两个类 的<span lang="EN-US">exec_state</span>函数中，就调用了对象的<span lang="EN-US">stop</span>和<span lang="EN-US">go</span>函数，让汽车保持原有的状态。<span lang="EN-US"> <br></span>至现在为止，<span lang="EN-US">Car</span>还没有接触到<span lang="EN-US">FSM</span>，所以我们需要提供一个接口，可以让它拥有一个<span lang="EN-US">FSM</span>：<span lang="EN-US"> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def attach_fsm(self, state, fsm): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.fsm = fsm <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.curr_state = state&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">我们还需要为<span lang="EN-US">Car</span>提供一个状态转换函数：<span lang="EN-US"> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def change_state(self, new_state, new_fsm): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.curr_state = new_state <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.fsm.exit_state(self) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.fsm = new_fsm <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.fsm.enter_state(self) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.fsm.exec_state(self)&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">为<span lang="EN-US">Car</span>提供一个保持状态的函数：<span lang="EN-US"> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def keep_state(self): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.fsm.exec_state(self)&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">现在只有两个状态，但我们知道需求随时会改动，所以我们最好弄一个状态机管理器来管理这些状态：<span lang="EN-US"> <br>class fsm_mgr(object): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def __init__(self): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self._fsms = {} <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self._fsms[0] = stop_fsm() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self._fsms[1] = run_fsm() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def get_fsm(self, state): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self._fsms[state] <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def frame(self, objs, state): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for obj in objs: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if state == obj.curr_state: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj.keep_state() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj.change_state(state, self._fsms[state])&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">fsm_mgr</span><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">最重要的函数就是<span lang="EN-US">frame</span>，在每一帧都被调用。在这里，<span lang="EN-US">frame</span>根据对象现在的状态和当前的输入决定让对象保持状态或者改变状态。<span lang="EN-US"> <br></span>这时候，我们的实例基本上完成了。但我们还要做一件事，就是建立一个世界（<span lang="EN-US">World</span>）来驱动状态机：<span lang="EN-US"> <br>class World(object): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def init(self): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self._cars = [] <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self._fsm_mgr = fsm_mgr() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.__init_car() <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def __init_car(self): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for i in xrange(1):&nbsp;&nbsp; # </span>生产汽车<span lang="EN-US"> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tmp = Car() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tmp.attach_fsm(0, self._fsm_mgr.get_fsm(0)) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self._cars.append(tmp) <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def __frame(self): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self._fsm_mgr.frame(self._cars, state_factory()) <br>&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def run(self): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while True: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.__frame() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(0.5)&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">从 代码可见，<span lang="EN-US">World</span>里有<span lang="EN-US">Car</span>对象，<span lang="EN-US">fsm_mgr</span>对象；在<span lang="EN-US">run</span>函数里，每<span lang="EN-US">0.5s</span>执行一次<span lang="EN-US">__frame</span>函数（<span lang="EN-US">FPS = 2</span>），而<span lang="EN-US">__frame</span>函数只是驱动了<span lang="EN-US">fsm_mgr</span>来刷新对象，新的命令是从<span lang="EN-US">state_factory</span>函数里取出来的，这个函数用以模拟驾驶员的操作（按下<span lang="EN-US">Stop</span>或者<span lang="EN-US">Run</span>按钮之一）：<span lang="EN-US"> <br>def state_factory(): <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return random.randint(0, 1)&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">现在我们就要初始化世界（<span lang="EN-US">World</span>）可以跑起我们的<span lang="EN-US">FSM</span>了！<span lang="EN-US"> <br>if __name__ == "__main__": <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; world = World() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; world.init() <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; world.run()&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">用<span lang="EN-US">python</span>解释器执行上面的代码，我们可以看到程序不停地输出<span lang="EN-US">Car</span>的状态：<span lang="EN-US"> <br>...... <br>Car8453392 exit run state! <br>Car8453392 enter stop state! <br>Car<?xml:namespace prefix = st1 /><st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="8453392" unitname="in">8453392 in</st1:chmetcnv> stop state! <br>Stop!!! <br>Car<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="8453392" unitname="in">8453392 in</st1:chmetcnv> stop state! <br>Stop!!! <br>Car8453392 exit stop state! <br>Car8453392 enter run state! <br>Car<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="8453392" unitname="in">8453392 in</st1:chmetcnv> run state! <br>Goooooo!!! <br>Car8453392 exit run state! <br>Car8453392 enter stop state! <br>Car<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="8453392" unitname="in">8453392 in</st1:chmetcnv> stop state! <br>Stop!!! <br>Car8453392 exit stop state! <br>Car8453392 enter run state! <br>Car<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="8453392" unitname="in">8453392 in</st1:chmetcnv> run state! <br>Goooooo!!! <br>Car<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="8453392" unitname="in">8453392 in</st1:chmetcnv> run state! <br>Goooooo!!! <br>Car8453392 exit run state! <br>Car8453392 enter stop state! <br>Car<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="8453392" unitname="in">8453392 in</st1:chmetcnv> stop state! <br>Stop!!! <br>Car<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="8453392" unitname="in">8453392 in</st1:chmetcnv> stop state! <br>Stop!!! <br>Car8453392 exit stop state! <br>Car8453392 enter run state! <br>Car<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="8453392" unitname="in">8453392 in</st1:chmetcnv> run state! <br>Goooooo!!! <br>......&nbsp; <o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 15.05pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">&nbsp; <br></span><b style="mso-bidi-font-weight: normal"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">结论 </span></b><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt"><br></span><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial; mso-font-kerning: 0pt">这时再回头来看看我们之前的问题：<span lang="EN-US"> <br>1</span>、玩家想要功能更多的<span lang="EN-US">Car</span>，比如掉头。<span lang="EN-US"> <br></span>我 们可以通过为<span lang="EN-US">Car</span>增加一个调头（<span lang="EN-US">back</span>）的方法来执行掉头，然后从<span lang="EN-US">base_fsm</span>中继承一个<span lang="EN-US">back_fsm</span>来处理调头。之后在<span lang="EN-US">fsm_mgr</span>里增 加一个<span lang="EN-US">back_fsm</span>实例，及让<span lang="EN-US">state_factory</span>产生调头指令。听起来似乎比之前<span lang="EN-US">while+if</span>的方式还要麻烦不少，其实不然，这里只有<span lang="EN-US"> back_fsm</span>和为<span lang="EN-US">fsm_mgr</span>增加<span lang="EN-US">back_fsm</span>实例才是特有的，其它步骤两种方法都要执行。<span lang="EN-US"> <br>2</span>、玩家要更多的<span lang="EN-US">Car</span>。<span lang="EN-US"> <br></span>这对于面向对象的<span lang="EN-US">FSM</span>实现就太简单了，我们只要把<span lang="EN-US">World.__init_car</span>里的生产数量修改一下就行了，要多少有多少。<span lang="EN-US"> <br>3</span>、玩家要更多型号的车，如<span lang="EN-US">Truck</span>。<span lang="EN-US"> <br></span>从<span lang="EN-US">Car</span>派生一个<span lang="EN-US">Truck</span>，然后增加装货、卸货的接口。最大的改动在于<span lang="EN-US">Truck</span>状态转换的时候需要一些判断，如不能直接从装货状态转换到开动状态，而是装货、停止再开动。<span lang="EN-US"> <br></span>通 过这几个简单的问题分析，我们可以看到，使用面向对象的方式来设计<span lang="EN-US">FSM</span>，在需求变更的时候，一般都只增删代码，而仍少需要改动已有代码，程序的扩展性、适应性和健壮性都得很大的提高；因此，在世界庞大、物种烦多、状态复杂且条件交错的游戏开发中应用面向对象的<span lang="EN-US">FSM</span>实在是明智之选。还有一点，面向对象的<span lang="EN-US"> FSM</span>可以非常容易地模拟消息机制，这有利于模块清晰化，更容易设计出正交的程序。 <span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="line-height: 150%"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial"><o:p>&nbsp;</o:p></span></p><img src ="http://www.cppblog.com/woaidongmao/aggbug/69415.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/woaidongmao/" target="_blank">肥仔</a> 2008-12-14 18:54 <a href="http://www.cppblog.com/woaidongmao/archive/2008/12/14/69415.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为Linux应用构造有限状态机方法</title><link>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69414.html</link><dc:creator>肥仔</dc:creator><author>肥仔</author><pubDate>Sun, 14 Dec 2008 10:40:00 GMT</pubDate><guid>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69414.html</guid><wfw:comment>http://www.cppblog.com/woaidongmao/comments/69414.html</wfw:comment><comments>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69414.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/woaidongmao/comments/commentRss/69414.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/woaidongmao/services/trackbacks/69414.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 有限自动机（Finite Automata Machine）是计算机科学的重要基石，它在软件开发领域内通常被称作有限状态机（Finite State Machine），是一种应用非常广泛的软件设计模式（Design Pattern）。本文介绍如何构建基于状态机的软件系统，以及如何利用Linux下的工具来自动生成实用的状态机框架。 　　一、什么是状态机 　　有限状态机是一种用来进行对象行为建模的工具...&nbsp;&nbsp;<a href='http://www.cppblog.com/woaidongmao/archive/2008/12/14/69414.html'>阅读全文</a><img src ="http://www.cppblog.com/woaidongmao/aggbug/69414.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/woaidongmao/" target="_blank">肥仔</a> 2008-12-14 18:40 <a href="http://www.cppblog.com/woaidongmao/archive/2008/12/14/69414.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏中的有限状态机</title><link>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69411.html</link><dc:creator>肥仔</dc:creator><author>肥仔</author><pubDate>Sun, 14 Dec 2008 09:56:00 GMT</pubDate><guid>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69411.html</guid><wfw:comment>http://www.cppblog.com/woaidongmao/comments/69411.html</wfw:comment><comments>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69411.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/woaidongmao/comments/commentRss/69411.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/woaidongmao/services/trackbacks/69411.html</trackback:ping><description><![CDATA[<p style="line-height: 150%"><span style="color: black; mso-bidi-font-family: tahoma">这是<span lang="EN-US">GAMEGEMS</span>中的第三章的第一部分，番的不好。你可以直接阅读原文。原本以为这是人工智能的部分，看到一半才发现只是一个简单的框架。如果你想学人工智能，这里没有，就不要浪费时间了。由于本人水平有限，其中难免会出现原则性的错误，希望指正。<span lang="EN-US"><br><br></span>关键字：有限状态机、状态、输入、状态转换、输出状态当前状态<span lang="EN-US"><br><br></span>一个有限状态机类<span lang="EN-US"><br></span>在这篇文章中，我们创建了一个通用的有限状态机（<span lang="EN-US">FSM</span>）的<span lang="EN-US">C++</span>类。有限状态机是计算机科学和数学理论的抽象，它在许多的方面是很有用处的。这里我们不去讲解有限状态机的理论上的知识。而是讲如何实现一个<span lang="EN-US">“</span>有限状态机<span lang="EN-US">”</span>，<span lang="EN-US">“</span>有限状态机<span lang="EN-US">”</span>在游戏的人工智能方面是很有用处的。<span lang="EN-US"><br><br>“</span>有限状态机<span lang="EN-US">”</span>是由有限的状态组成的一个机制。一个<span lang="EN-US">“</span>状态<span lang="EN-US">”</span>就是一个状况。你考虑一下门；它的<span lang="EN-US">“</span>状态<span lang="EN-US">”</span>有<span lang="EN-US">“</span>开<span lang="EN-US">”</span>或<span lang="EN-US">“</span>关<span lang="EN-US">”</span>以及<span lang="EN-US">“</span>锁<span lang="EN-US">”</span>与<span lang="EN-US">“</span>未锁<span lang="EN-US">”</span>。<span lang="EN-US"><br><br></span>对于一个<span lang="EN-US">“</span>有限状态机<span lang="EN-US">”</span>，它应该有一个<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>。这个<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>可以影响<span lang="EN-US">“</span>状态转换<span lang="EN-US">”</span>。<span lang="EN-US">“</span>有限状态机<span lang="EN-US">”</span>应该有一个简单（或复杂）的状态转换函数，这个函数可以决定什么状态可以变成<span lang="EN-US">“</span>当前状态<span lang="EN-US">”</span>。<span lang="EN-US"><br><br></span>这个当前的新状态被称为<span lang="EN-US">“</span>有限状态机<span lang="EN-US">”</span>的<span lang="EN-US">“</span>状态转换<span lang="EN-US">”</span>的<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>。如果你对这个概念有些迷惑，就把<span lang="EN-US">“</span>门<span lang="EN-US">”</span>做为理解<span lang="EN-US">“</span>有限状态机<span lang="EN-US">”</span>的例子。当一个<span lang="EN-US">“</span>门<span lang="EN-US">”</span>处于<span lang="EN-US">“</span>关闭<span lang="EN-US">”</span>状态和<span lang="EN-US">“</span>锁<span lang="EN-US">”</span>状态，当你输入了<span lang="EN-US">“</span>使用钥匙<span lang="EN-US">”</span>时，门的状态可以变成<span lang="EN-US">“</span>未锁<span lang="EN-US">”</span>状态（即<span lang="EN-US">“</span>状态转换<span lang="EN-US">”</span>的输出状态，也就是门的当前状态）。当你输入了<span lang="EN-US">“</span>使用手<span lang="EN-US">”</span>时，门的状态可以转换成<span lang="EN-US">“</span>开<span lang="EN-US">”</span>的状态。当门处于<span lang="EN-US">“</span>开<span lang="EN-US">”</span>的状态时，我们输入<span lang="EN-US">“</span>使用手<span lang="EN-US">”</span>时，会使门的状态重新回到<span lang="EN-US">“</span>关<span lang="EN-US">”</span>的状态。当<span lang="EN-US">“</span>门<span lang="EN-US">”</span>处于<span lang="EN-US">“</span>关<span lang="EN-US">”</span>的状态时，我们输入<span lang="EN-US">“</span>使用钥匙<span lang="EN-US">”</span>时，这将会使门重新回到<span lang="EN-US">“</span>锁<span lang="EN-US">”</span>的状态。当门处于<span lang="EN-US">“</span>锁<span lang="EN-US">”</span>的状态，我们输入<span lang="EN-US">“</span>使用手<span lang="EN-US">”</span>，就不能把门的状态转换到<span lang="EN-US">“</span>开<span lang="EN-US">”</span>的状态，门仍然会保持<span lang="EN-US">“</span>锁<span lang="EN-US">”</span>的状态。还有，当门处于<span lang="EN-US">“</span>开<span lang="EN-US">”</span>的状态时，我们输入<span lang="EN-US">“</span>使用钥匙<span lang="EN-US">”</span>是不能把门的状态转换成<span lang="EN-US">“</span>锁<span lang="EN-US">”</span>的状态的。<span lang="EN-US"><br><br></span>总之，<span lang="EN-US">“</span>有限状态机<span lang="EN-US">”</span>是一个有限的状态组成的，其中的一个状态是<span lang="EN-US">“</span>当前状态<span lang="EN-US">”</span>。<span lang="EN-US">“</span>有限状态机<span lang="EN-US">”</span>可以接受一个<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>，这个<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>的结果将导致一个<span lang="EN-US">“</span>状态转换<span lang="EN-US">”</span>的发生（即从<span lang="EN-US">“</span>当前状态<span lang="EN-US">”</span>转换到<span lang="EN-US">“</span>输出<span lang="EN-US">”</span>状态）。这个<span lang="EN-US">“</span>状态转换<span lang="EN-US">”</span>是基于<span lang="EN-US">“</span>状态转换函数<span lang="EN-US">”</span>的。状态转换完成之后，<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>即变成了<span lang="EN-US">“</span>当前状态<span lang="EN-US">”</span>。<span lang="EN-US"><br><br></span>输入 状态转换函数<span lang="EN-US"><br></span>当前状态<span lang="EN-US">-----</span>》状态转换<span lang="EN-US">-------------</span>》输出状态（当前状态）<span lang="EN-US"><br><br></span>那么，人们是如何将这个概念应用到游戏的<span lang="EN-US">AI</span>系统中的呢？有限状态机的功能很多：管理游戏世界、模拟<span lang="EN-US">NPC</span>的思维、维护游戏状态、分析玩游戏的人的输入，或者管理一个对象的状态。<span lang="EN-US"><br><br></span>假如在一个冒险游戏中有一个<span lang="EN-US">NPC</span>，名字可以叫<span lang="EN-US">MONSTER</span>。我们可以先假设这个<span lang="EN-US">MONSTER</span>在游戏中有如下的状态：<span lang="EN-US">BERSERK</span>、<span lang="EN-US">RAGE</span>、<span lang="EN-US">MAD</span>、<span lang="EN-US">ANNOYED</span>以及<span lang="EN-US">UNCARING</span>。（前几个状态不好分别）。假设，<span lang="EN-US">MONSTER</span>对于不同的状态可以执行不同的操作，并且假设你已经有了这些不同操作的代码。我们这时可以使用<span lang="EN-US">“</span>有限状态机<span lang="EN-US">”</span>来模拟这个<span lang="EN-US">MONSTER</span>的行为了。只要我们给出不同的<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>，<span lang="EN-US">MONSTER</span>就会做出不同的反应。我们再来指出这些<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>是什么：<span lang="EN-US">PLAYER SEEN</span>、<span lang="EN-US">PLAYER ATTACKS</span>、<span lang="EN-US">PLAYERGONE</span>、<span lang="EN-US">MONSTER HURT</span>、<span lang="EN-US">MONSTER HEALED</span>。这样我们可以得到一个状态转换的表格，如下：游戏状态转换表：<span lang="EN-US"><br><br></span>当前状态 输入 输出状态<span lang="EN-US"><br></span></span><span lang="EN-US" style="color: black">UNCARING PLAYER SEEN ANNOYED<br>UNCARING PLAYER ATTACKS MAD<br>MAD MONSTER HURT RAGE<br>MAD MONSTER HEALED UNCARING<br>RAGE MONSTER HURT BERSERK<br>RAGE MONSTER HEALED ANNOYED<br>BERSERK MONSTER HURT BERSERK<br>BERSERK MONSTER HEALED RAGE<br>ANNOYED PLAYER GONE UNCARING<br>ANNOYED PLAYER ATTACKS RAGE<br>ANNOYED MONSTER HEALED UNCARING<br></span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br></span><span style="color: black; mso-bidi-font-family: tahoma">根据上面的这个表格，我们可以很容易的画出一个<span lang="EN-US">MONSTER</span>的<span lang="EN-US">“</span>状态转换图<span lang="EN-US">”</span>，<span lang="EN-US">MONSTER</span>的每一个状态就是图中的顶点。<span lang="EN-US"><br></span>因此，根据当前状态和对<span lang="EN-US">FSM</span>的输入，<span lang="EN-US">MONSTER</span>的状态将被改变。这时根据<span lang="EN-US">MONSTER</span>的状态执行相应操作的代码（假设已经实现）将被执行，这时<span lang="EN-US">MONSTER</span>好像是具备了人工智能。显然，我们可以定义更多的<span lang="EN-US">“</span>状态<span lang="EN-US">”</span>，写出更多的<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>，写出更多的<span lang="EN-US">“</span>状态转换<span lang="EN-US">”</span>，这样，<span lang="EN-US">MONSTER</span>可以表现的更真实，生动，当然，这些游戏的规则问题应该是策划制定的。<span lang="EN-US"><br><br>FSMclass</span>以及<span lang="EN-US">FSMstate<br><br></span>现在，我们如何把这些方法变成现实？使用<span lang="EN-US">FSMclass</span>和它的组成部分<span lang="EN-US">FSMstate</span>可以实现这些想法。<span lang="EN-US"><br><br></span>定义<span lang="EN-US">FSMstate<br></span></span><span lang="EN-US" style="color: black">class FSMstate<br>{<br>&nbsp;&nbsp;&nbsp; unsigned m_usNumberOfTransition; //</span><span style="color: black">状态的最大数<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; int* m_piInputs; //</span>为了转换而使用的输入数组<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; int* m_piOutputState; //</span>输出状态数组<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; int m_iStateID; //</span>这个状态的唯一标识符<span lang="EN-US"><br>public:<br>&nbsp;&nbsp;&nbsp; //</span>一个构造函数，可以接受这个状态的<span lang="EN-US">ID</span>和它支持的转换数目<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; FSMstate(int iStateID,unsigned usTransitions);<br>&nbsp;&nbsp;&nbsp; //</span>析构函数，清除分配的数组<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; ~FSMstate();<br>&nbsp;&nbsp;&nbsp; //</span>取这个状态的<span lang="EN-US">ID<br>&nbsp;&nbsp;&nbsp; int GetID(){return m_iStateID;}<br>&nbsp;&nbsp;&nbsp; //</span>向数组中增加状态转换<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; void AddTransition(int iInput,int iOutputID);<br>&nbsp;&nbsp;&nbsp; //</span>从数组中删除一个状态转换<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; void DeleteTransition(int iOutputID);<br>&nbsp;&nbsp;&nbsp; //</span>进行状态转换并得到输出状态<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; int GetOutput(int iInput);<br>};</span></span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span style="color: black; mso-bidi-font-family: tahoma">对这个类的分析：<span lang="EN-US"><br></span>功能：主要是实现与一个状态相关的各种操作。我们前面假设了<span lang="EN-US">MONSTER</span>的各种状态：<span lang="EN-US"><br></span></span><span lang="EN-US" style="color: black">#define STATE_ID_UNCARING 1<br>#define STATE_ID_MAD 2<br>#define STATE_ID_RAGE 3<br>#define STATE_ID_BERSERK 4<br>#define STATE_ID_ANNOYED 5<br></span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br></span><span style="color: black; mso-bidi-font-family: tahoma">状态转换所需的输入有：<span lang="EN-US"><br></span></span><span lang="EN-US" style="color: black">#define INPUT_ID_PLAYER_SEEN 1<br>#define INPUT_ID_PLAYER_ATTACK 2<br>#define INPUT_ID_PLAYER_GONE 3<br>#define INPUT_ID_MONSTER_HURT 4<br>#define INPUT_ID_MONSTER_HEALED 5</span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span style="color: black; mso-bidi-font-family: tahoma">以上是五个状态的标识符。<span lang="EN-US"><br></span>我们就要声明<span lang="EN-US">5</span>个<span lang="EN-US">FSMstate</span>的实例，每一个实例代表一个状态和与之有关的操作。假设我们先处理状态<span lang="EN-US">STATE_ID_MAD<br></span>类成员变量<span lang="EN-US">m_iStateID</span>就等于<span lang="EN-US">STATE_ID_MAD</span>类成员变量<span lang="EN-US">m_usNumberOfTransition</span>就是可由这个状态转换成的状态的个数，前面有一个表，其中有两个状态可以由这个状态产生，它们分别是<span lang="EN-US">STATE_ID_UNCARING</span>和<span lang="EN-US">STATE_ID_RAGE</span>。<span lang="EN-US"><br></span>这时，<span lang="EN-US">m_usNumberOfTransition</span>等于<span lang="EN-US">2</span>。<span lang="EN-US"><br><br>m_piInputs</span>是一个指针变量，它保存与这个状态相关的输入，在前面的表中我们知道与<span lang="EN-US">STATE_ID_MAD</span>相关的输入为<span lang="EN-US"><br>INPUT_ID_MONSTER_HURT</span>和<span lang="EN-US">INPUT_ID_MONSTER_HEALED</span>，因此<span lang="EN-US">m_piInputs</span>中存放的是这两个数据。<span lang="EN-US"><br></span>而<span lang="EN-US">m_piOutputState</span>存放的是与<span lang="EN-US">STATE_ID_MAD</span>相关的状态，即<span lang="EN-US">STATE_ID_RAGE</span>和<span lang="EN-US">STATE_ID_UNCARING</span>，这样，<span lang="EN-US">m_piOutputState</span>中存放的数据便是这两个值。<span lang="EN-US"><br><br></span>以上是对成员变量的解释，下面解释成员函数：<span lang="EN-US"><br></span>构造函数<span lang="EN-US"><br></span></span><span lang="EN-US" style="color: black">FSMstate::FSMstate(int iStateID,unsigned usTransitions)<br>{<br>&nbsp;&nbsp;&nbsp; if(!usTransitions) //</span><span style="color: black">如果给出的转换数量为<span lang="EN-US">0</span>，就算为<span lang="EN-US">1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_usNumberOfTransitions=1;<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_usNumberOfTransitions=usTransitions;<br><br>&nbsp;&nbsp;&nbsp; //</span>将状态的<span lang="EN-US">ID</span>保存起来<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; m_iStateID=iStateID;<br><br>&nbsp;&nbsp;&nbsp; //</span>分配内存空间<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_piInputs=new int[m_usNumberOfTransitions];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;m_usNumberOfTransitions;++i)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_piInputs[i]=0;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; catch(...)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_piOutputState=new int[m_usNumberOfTransition];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;m_usNumberOfTransitions;++i)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_piOutputState[i]=0;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; catch(...)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delete [] m_piInputs;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw;<br>&nbsp;&nbsp;&nbsp; }<br>}</span></span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span style="color: black; mso-bidi-font-family: tahoma">这就是构造函数，在<span lang="EN-US">FSMstate</span>类中共有四个成员变量，在这个函数中全部被初始化了。<span lang="EN-US">FSMstate</span>是一个类，是否还记得<span lang="EN-US">MONSTER</span>的状态（如<span lang="EN-US">MAD</span>、<span lang="EN-US">UNCARING</span>）。这个类就是实现对<span lang="EN-US">MONSTER</span>的一个状态的管理的。假如这个状态是<span lang="EN-US">STATE_ID_MAD</span>，与这个状态相关的状态有两个，上面已经讲过了。这时我们给成员变量赋值，在这个具体例子中它们的值如下：<span lang="EN-US"><br><br></span></span><span lang="EN-US" style="color: black">m_usNumberOfTransition=2<br>m_piInput[0]=0;<br>m_piInput[1]=0;<br>m_piOutputState[0]=0;<br>m_piOutputState[1]=0;<br>m_iStateID=STATE_ID_MAD;<br></span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br></span><span style="color: black; mso-bidi-font-family: tahoma">析构函数：<span lang="EN-US"><br></span></span><span lang="EN-US" style="color: black">FSMState::~FSMState()<br>{<br>&nbsp;&nbsp;&nbsp; delete [] m_piInputs;<br>&nbsp;&nbsp;&nbsp; delete [] m_piOutputState;<br>}</span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br></span><span style="color: black; mso-bidi-font-family: tahoma">析构函数将动态分配的存储空间释放了。<span lang="EN-US"><br><br></span></span><span lang="EN-US" style="color: black">void FSMstate::AddTransition(int iInput,int iOutputID)<br>{<br>&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;m_usNumberOfTransitions;++i)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!m_piOutputState[i]) break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(i&lt;m_usNumberOfTransition)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_piOutputState[i]=iOutputID;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_piInputs[i]=iInput;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>}</span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span style="color: black; mso-bidi-font-family: tahoma">这个函数给两个前面构造函数动态分配的空间加入数据，首先要找到两个数组中找到适当的位置，之后，如果位置是合法的<span lang="EN-US"><br></span>我们就可以把数据加入这两个数组中。因为<span lang="EN-US">STATE_ID_MAD</span>与两个状态有关，因此，我们可以调用两次这个函数，把这两个状态加入到类中：<span lang="EN-US"><br><br></span></span><span lang="EN-US" style="color: black">AddTransition(INPUT_ID_MONSTER_HURT,STATE_ID_RAGE);<br>AddTransition(INPUT_ID_MONSTER_HEALED,STATE_ID_UNCARING)</span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br></span><span style="color: black; mso-bidi-font-family: tahoma">这样，与状态<span lang="EN-US">STATE_ID_MAD</span>相关的<span lang="EN-US">“</span>状态<span lang="EN-US">”</span>和<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>也加入了。<span lang="EN-US"><br><br></span></span><span lang="EN-US" style="color: black">void FSMstate::DeleteTransition(int iOutputID)<br>{<br>&nbsp;&nbsp;&nbsp; // </span><span style="color: black">遍历每一个输出状态<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;m_usNumberOfTransitions;++i)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>如果找到输出状态，退出循环<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(m_piOutputState[i]==iOutputID)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; //</span>如果没有找到输出状态，返回<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; if(i&gt;=m_usNumberOfTransitions)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br>&nbsp;&nbsp;&nbsp; //</span>将输出状态的内容置<span lang="EN-US">0<br>&nbsp;&nbsp;&nbsp; m_piInputs[i]=0;<br>&nbsp;&nbsp;&nbsp; m_piOutputState[i]=0;<br><br>&nbsp;&nbsp;&nbsp; //</span>被删除的输出状态的后面的输出状态前移<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; for(;i&lt;(m_usNumberOfTransition-1);++i)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!m_piOUtputState[i])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_piInputs[i]=m_piInputs[i+1];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_piOutputState[i]=m_piOutputState[i+1];<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; //</span>最后面的输出状态置<span lang="EN-US">0<br>&nbsp;&nbsp;&nbsp; m_piInputs[i]=0;<br>&nbsp;&nbsp;&nbsp; m_piOutputState[i]=0;<br>}</span></span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span style="color: black; mso-bidi-font-family: tahoma">这个函数是要删除与一个状态相关的输出状态。设一个状态<span lang="EN-US">STATE_ID_MAD</span>，与之相关的状态有两个<span lang="EN-US">STATE_ID_RAGE,STATE_ID_UNCARING</span>，当然这是经过初始化以及前面的添加状态函数之后，产生了这两个相关的状态。你想删除哪一个？如果你想删除相关的输出状态，只要在删除函数中指出那个状态即可，例如：<span lang="EN-US"><br><br></span></span><span lang="EN-US" style="color: black">DeleteTransition(STATE_ID_RAGE);</span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br></span><span style="color: black; mso-bidi-font-family: tahoma">你就可以删除输出状态<span lang="EN-US">STATE_ID_RAGE</span>了。<span lang="EN-US"><br><br></span></span><span lang="EN-US" style="color: black">int FSMstate::GetOutput(int iInput)<br>{<br>&nbsp;&nbsp;&nbsp; //</span><span style="color: black">先给输出状态赋值（如果未找到与输入对应的输出状态时，返回这个值）<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; int iOutputID=m_iStateID;<br><br>&nbsp;&nbsp;&nbsp; //</span>遍历输出状态<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;m_usNumberOfTransitions;++i)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>如果没找到，退出循环<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!m_piOutputState[i])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span>如果找到了与<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>相对应的<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>，进行赋值。<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(iInput==m_piInputs[i])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iOutputID=m_piOutputState[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; //</span>返回<span lang="EN-US">“</span>输出状态<span lang="EN-US">”<br>&nbsp;&nbsp;&nbsp; return(iOutputID);<br>}</span></span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span style="color: black; mso-bidi-font-family: tahoma">这个函数功能是返回与<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>相对应的<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>的标识。如果没有与<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>相对应的<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>，返回原来的状态，如果有与之对应的<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>，返回这个状态的<span lang="EN-US">ID</span>。<span lang="EN-US"><br><br></span>下面定义的是<span lang="EN-US">FSMclass</span>，这个类用于维护<span lang="EN-US">FSMstate</span>对象集合。<span lang="EN-US"><br></span></span><span lang="EN-US" style="color: black">class FSMclass<br>{<br>&nbsp;&nbsp;&nbsp; State_Map m_map; //</span><span style="color: black">包括了状态机的所有状态<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; int m_iCurrentState; //</span>当前状态的<span lang="EN-US">ID<br>public:<br>&nbsp;&nbsp;&nbsp; FSMclass(int iStateID); //</span>初始化状态<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; ~FSMclass()<br>&nbsp;&nbsp;&nbsp; //</span>返回当前状态<span lang="EN-US">ID<br>&nbsp;&nbsp;&nbsp; int GetCurrentState() {return m_iCurrentState;}<br>&nbsp;&nbsp;&nbsp; //</span>设置当前状态<span lang="EN-US">ID<br>&nbsp;&nbsp;&nbsp; void SetCurrentState(int iStateID) {m_iCurrentState=iStateID;}<br>&nbsp;&nbsp;&nbsp; //</span>返回<span lang="EN-US">FSMstate</span>对象指针<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; FSMstate* GetState(int iStateID);<br>&nbsp;&nbsp;&nbsp; //</span>增加状态对象指针<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; void AddState(FSMstate* pState);<br>&nbsp;&nbsp;&nbsp; //</span>删除状态对象指针<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; void DeleteState(int iStateID);<br>&nbsp;&nbsp;&nbsp; //</span>根据<span lang="EN-US">“</span>当前状态<span lang="EN-US">”</span>和<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>完成<span lang="EN-US">“</span>状态<span lang="EN-US">”</span>的转换。<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; int StateTransition(int iInput);<br>};</span></span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br>FSMclass::m_map</span><span style="color: black; mso-bidi-font-family: tahoma">是<span lang="EN-US">FSMstate</span>对象的集合，是从<span lang="EN-US">STL&lt;map&gt;</span>中实现的。<span lang="EN-US"><br>FSMclass::m_iCurrentState</span>是<span lang="EN-US">FSMstate</span>对象的状态标识，是<span lang="EN-US">“</span>有限状态机<span lang="EN-US">”</span>的当前状态。<span lang="EN-US"><br><br>FSMclass::GetCurrentState()</span>可以用之访问当前的<span lang="EN-US">FSMstate</span>对象的状态的标识符。<span lang="EN-US"><br>FSMclass::SetCurrentState()</span>可以设置当前<span lang="EN-US">FSMstate</span>对象的状态的标识符。<span lang="EN-US"><br>FSMclass::GetState()</span>可以取得有限状态机中的任何<span lang="EN-US">FSMstate</span>对象的指针。<span lang="EN-US"><br>FSMclass::AddState()</span>增加有限状态机中的<span lang="EN-US">FSMstate</span>对象。<span lang="EN-US"><br>FSMclass::DeleteState()</span>删除有限状态机中的<span lang="EN-US">FSMstate</span>对象<span lang="EN-US"><br>FSMclass::StateTransition()</span>初始化状态转换，根据输入返回输出状态。<span lang="EN-US"><br><br></span>这个类使用了<span lang="EN-US">STL</span>，我不知道它怎么用：）。听说是高人才使用它，高人起码要写过上万行的代码。因此不能详细介绍这个类了<span lang="EN-US"><br><br></span>总之，可以这么理解这两个类<span lang="EN-US">FSMstate,FSMclass.FSMstate</span>代表了一个状态以及和状态相关的数据和操作。如在<span lang="EN-US">MONSTER</span>中有五个状态，我们就要声明五个类的对象，每个对象中包括了与这个状态相关的状态，输入和各种转换函数。可以说<span lang="EN-US">FSMstate</span>是对每一个状态的封装（包括相关数据和操作），游戏中的对象有多少状态，就要声明多少个<span lang="EN-US">FSMstate</span>对象。而<span lang="EN-US">FSMclass</span>则是对这若干个<span lang="EN-US">FSMstate</span>对象（这个例子中<span lang="EN-US">MONSTER</span>有五个状态）进行的封装。在<span lang="EN-US">FSMclass</span>中指明了若干个<span lang="EN-US">FSMstate</span>中哪一个是当前的<span lang="EN-US">MONSTER</span>拥有的状态并且可以设置，得到以及删除状态，并且可以进行状态间的转换。<span lang="EN-US"><br><br></span>总之：游戏中的<span lang="EN-US">MONSTER</span>有多少状态，游戏中就要声明多少的<span lang="EN-US">FSMstate</span>对象，每一个<span lang="EN-US">FSMstate</span>对象包括了与特定的状态相关的数据和操作。而<span lang="EN-US">FSMclass</span>只有一个，它用于协调若干个<span lang="EN-US">FSMstate</span>之间的关系和操作。<span lang="EN-US"><br><br></span>下面是如何在游戏中使用两个类的例子：<span lang="EN-US"><br><br></span>首先是创建<span lang="EN-US">FSMstate</span>对象（若干个），有多少状态就要循环多少次，下面是增加<span lang="EN-US">STATE_ID_UNCARING</span>状态的例子：<span lang="EN-US"><br></span></span><span lang="EN-US" style="color: black">FSMstate* pFSMstate=NULL;</span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br></span><span lang="EN-US" style="color: black">//</span><span style="color: black">创建状态<span lang="EN-US"><br>try<br>{<br>&nbsp;&nbsp;&nbsp; //</span>第一个参数是增加状态的标识，第二个参数指明了与这个<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; //</span>状态相关的状态的个数。<span lang="EN-US"><br>&nbsp;&nbsp;&nbsp; pFSMstate=new FSMstate(STATE_ID_UNCARING,2);<br>}<br>catch(...)<br>{<br>&nbsp;&nbsp;&nbsp; throw;<br>}</span></span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span lang="EN-US" style="color: black">//</span><span style="color: black">之后给这个状态加入相关的<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>和<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span></span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br></span><span lang="EN-US" style="color: black">pFSMstate-&gt;AddTransition(INPUT_ID_PLAYER_SEEN,STATE_ID_ANNOYED);<br>pFSMstate-&gt;AddTransition(INPUT_ID_PLAYER_ATTACKS,STATE_ID_MAD);</span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span style="color: black; mso-bidi-font-family: tahoma">这个函数指明了与特定状态相关的<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>和<span lang="EN-US">“</span>输出状态<span lang="EN-US">”<br></span>比如第一个函数，它表明如果我要输入一个<span lang="EN-US">INPUT_ID_PLAYER_SEEN</span>，这时就会产生一个输出状态，<span lang="EN-US">STATE_ID_ANNOYED</span>。<span lang="EN-US"><br></span>我们应该为每一个状态做上面的事情，这里就略过了。之后我们要声明一个<span lang="EN-US">FSMclass</span>对象，用于协调上面的<span lang="EN-US">FSMstate</span>对象之间的关系。<span lang="EN-US"><br><br></span></span><span lang="EN-US" style="color: black">try<br>{<br>&nbsp;&nbsp;&nbsp; m_pFSMclass=new FSMclass(STATE_ID_UNCARING);<br>}<br>catch(...)<br>{<br>&nbsp;&nbsp;&nbsp; throw;<br>}</span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span style="color: black; mso-bidi-font-family: tahoma">上面指明了<span lang="EN-US">MONSTER</span>的当前状态是<span lang="EN-US">STATE_ID_UNCARING</span>最后将<span lang="EN-US">FSMstate</span>对象分别加入到<span lang="EN-US">FSMclass</span>中。<span lang="EN-US"><br><br></span>下面介绍如何使用<span lang="EN-US">FSMclass<br></span>使用十分简单，只要我们给出一个<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>，之后，我们便可以得到一个<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>，根据这个<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>我们执行相应的操作，最后，把这个<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>变成<span lang="EN-US">MONSTER</span>的当前状态。<span lang="EN-US"><br><br></span>在游戏中发生了一些事情，如玩游戏的人指出他控制的人进攻<span lang="EN-US">MONSTER</span>（用鼠标点击了<span lang="EN-US">MONSTER</span>），这时会产生一个<span lang="EN-US">“</span>输入<span lang="EN-US">”iInputID=INPUT_ID_PLAYER_ATTACK;<br><br></span>这时，我们调用状态转换函数：<span lang="EN-US"><br></span></span><span lang="EN-US" style="color: black">m_iOutputState=m_pFSMclass-&gt;StateTransition(iInputID);</span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span style="color: black; mso-bidi-font-family: tahoma">这时，我们的<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>对<span lang="EN-US">MONSTER</span>产生了刺激，产生了一个<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>。这时我们根据这个输出状态调用相应的代码执行就可以了，这时的<span lang="EN-US">MONSTER</span>好像有应反了，我们说它有了简单的智能。<span lang="EN-US"><br><br></span></span><span lang="EN-US" style="color: black">if(m_iOutputState==STATE_ID_MAD)<br>{<br>&nbsp;&nbsp;&nbsp; //some code for the monster to act mad<br>}</span><span lang="EN-US" style="color: black; mso-bidi-font-family: tahoma"><br><br></span><span style="color: black; mso-bidi-font-family: tahoma">当然，我们也应该把其它状态执行的操作也写出来，但只写一个就可以了。使用这个状态机就是这么简单。总之，<span lang="EN-US">FSMclass</span>不是全部的人工智能，相反，它只是一个框架，一个开始智能需要的还很多。只要你可以分出<span lang="EN-US">“</span>状态<span lang="EN-US">”</span>，并且知道什么<span lang="EN-US">“</span>输入<span lang="EN-US">”</span>产生什么<span lang="EN-US">“</span>输出状态<span lang="EN-US">”</span>就可以了，当然这是一个游戏的规则，策划应当完成这个部分？</span><span lang="EN-US" style="color: black"><?xml:namespace prefix = o /><o:p></o:p></span></p> <p class="MsoNormal" style="line-height: 150%"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial"><o:p>&nbsp;</o:p></span></p><img src ="http://www.cppblog.com/woaidongmao/aggbug/69411.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/woaidongmao/" target="_blank">肥仔</a> 2008-12-14 17:56 <a href="http://www.cppblog.com/woaidongmao/archive/2008/12/14/69411.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>有限状态机的思考</title><link>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69410.html</link><dc:creator>肥仔</dc:creator><author>肥仔</author><pubDate>Sun, 14 Dec 2008 09:54:00 GMT</pubDate><guid>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69410.html</guid><wfw:comment>http://www.cppblog.com/woaidongmao/comments/69410.html</wfw:comment><comments>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69410.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/woaidongmao/comments/commentRss/69410.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/woaidongmao/services/trackbacks/69410.html</trackback:ping><description><![CDATA[<p class="MsoNormal" style="word-break: break-all; line-height: 150%; mso-pagination: widow-orphan"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">有限状态机（<span lang="EN-US">Finite State Machine</span>或者<span lang="EN-US">Finite State Automata)</span>是软件领域中一种重要的工具，很多东西的模型实际上就是有限状态机。<span lang="EN-US"><br><br></span>最近看了一些游戏编程<span lang="EN-US">AI</span>的材料，感觉游戏中的<span lang="EN-US">AI</span>，第一要说的就是有限状态机来实现精灵的<span lang="EN-US">AI</span>，然后才是<span lang="EN-US">A*</span>寻路，其他学术界讨论比较多的神经网络、模糊控制等问题还不是很热。<span lang="EN-US"><br><br>FSM</span>的实现方式：<span lang="EN-US"><br><b style="mso-bidi-font-weight: normal">1</b></span><b style="mso-bidi-font-weight: normal">）<span lang="EN-US"> switch/case</span>或者<span lang="EN-US">if/else</span></b><span lang="EN-US"><br></span>这无意是最直观的方式，使用一堆条件判断，会编程的人都可以做到，对简单小巧的状态机来说最合适，但是毫无疑问，这样的方式比较原始，对庞大的状态机难以维护。<span lang="EN-US"><br><br><b style="mso-bidi-font-weight: normal"><font color="#ff0000">2</font></b></span><b style="mso-bidi-font-weight: normal"><font color="#ff0000">） 状态表</font></b><span lang="EN-US"><br></span><font color="#ff0000">维护一个二维状态表，横坐标表示当前状态，纵坐标表示输入，表中一个元素存储下一个状态和对应的操作。这一招易于维护，但是运行时间和存储空间的代价较大。</font><span lang="EN-US"><br><br><b style="mso-bidi-font-weight: normal">3</b></span><b style="mso-bidi-font-weight: normal">） 使用<span lang="EN-US">State Pattern</span></b><span lang="EN-US"><br></span>使用<span lang="EN-US">State Pattern</span>使得代码的维护比<span lang="EN-US">switch/case</span>方式稍好，性能上也不会有很多的影响，但是也不是<span lang="EN-US">100</span>％完美。不过<span lang="EN-US">Robert C. Martin</span>做了两个自动产生<span lang="EN-US">FSM</span>代码的工具，<span lang="EN-US">for java</span>和<span lang="EN-US">for C++</span>各一个，在<span lang="EN-US">http://www.objectmentor.com/resources/index</span>上有免费下载，这个工具的输入是纯文本的状态机描述，自动产生符合<span lang="EN-US">State Pattern</span>的代码，这样<span lang="EN-US">developer</span>的工作只需要维护状态机的文本描述，每必要冒引入<span lang="EN-US">bug</span>的风险去维护<span lang="EN-US">code</span>。<span lang="EN-US"><br><br><b style="mso-bidi-font-weight: normal">4</b></span><b style="mso-bidi-font-weight: normal">） 使用宏定义描述状态机</b><span lang="EN-US"><br></span>一般来说，<span lang="EN-US">C++</span>编程中应该避免使用<span lang="EN-US">#define</span>，但是这主要是因为如果用宏来定义函数的话，很容易产生这样那样的问题，但是巧妙的使用<span lang="EN-US">,</span>还是能够产生奇妙的效果。<span lang="EN-US">MFC</span>就是使用宏定义来实现大的架构的。<span lang="EN-US"><br></span>在实现<span lang="EN-US">FSM</span>的时候，可以把一些繁琐无比的<span lang="EN-US">if/else</span>还有花括号的组合放在宏中，这样，在代码中可以<span lang="EN-US">3</span>）中状态机描述文本一样写，通过编译器的预编译处理产生<span lang="EN-US">1</span>）一样的效果，我见过产生<span lang="EN-US">C</span>代码的宏，如果要产生<span lang="EN-US">C++</span>代码，己软<span lang="EN-US">MFC</span>可以，那么理论上也是可行的。 <span lang="EN-US"><?xml:namespace prefix = o /><o:p></o:p></span></span></p> <p class="MsoNormal"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: arial"><o:p>&nbsp;</o:p></span></p><img src ="http://www.cppblog.com/woaidongmao/aggbug/69410.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/woaidongmao/" target="_blank">肥仔</a> 2008-12-14 17:54 <a href="http://www.cppblog.com/woaidongmao/archive/2008/12/14/69410.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>状态机</title><link>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69409.html</link><dc:creator>肥仔</dc:creator><author>肥仔</author><pubDate>Sun, 14 Dec 2008 09:53:00 GMT</pubDate><guid>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69409.html</guid><wfw:comment>http://www.cppblog.com/woaidongmao/comments/69409.html</wfw:comment><comments>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69409.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/woaidongmao/comments/commentRss/69409.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/woaidongmao/services/trackbacks/69409.html</trackback:ping><description><![CDATA[<p>&nbsp; <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt"><font color="#ff0000">关于状态机的一个极度确切的描述是它是一个有向图形，由一组节点和一组相应的转移函数组成。状态机通过响应一系列事件而<span lang="EN-US">“</span>运行<span lang="EN-US">”</span>。每个事件都在属于<span lang="EN-US">“</span>当前<span lang="EN-US">” </span>节点的转移函数的控制范围内，其中函数的范围是节点的一个子集。函数返回<span lang="EN-US">“</span>下一个<span lang="EN-US">”</span>（也许是同一个）节点。这些节点中至少有一个必须是终态。当到达终态， 状态机停止。</font> <span lang="EN-US"><?xml:namespace prefix = o /><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt">包含一组状态集（<span lang="EN-US">states</span>）、一个起始状态（<span lang="EN-US">start state</span>）、一组输入符号集（<span lang="EN-US">alphabet</span>）、一个映射输入符号和当前状态到下一状态的转换函数（<span lang="EN-US">transition function</span>）的计算模型。当输入符号串，模型随即进入起始状态。它要改变到新的状态，依赖于转换函数。在有限状态机中，会有有许多变量，例如，状态机有很多与动作（<span lang="EN-US">actions</span>）转换<span lang="EN-US">(Mealy</span>机<span lang="EN-US">)</span>或状态（摩尔机）关联的动作，多重起始状态，基于没有输入符号的转换，或者指定符号和状态（非定有 限状态机）的多个转换，指派给接收状态（识别者）的一个或多个状态，等等。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt"><span style="mso-spacerun: yes">&nbsp;</span><o:p></o:p></span></p> <p class="MsoNormal" style="line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt">　　<font color="#ff0000">传统应用程序的控制流程基本是顺序的：遵循事先设定的逻辑，从头到尾地执行。很少有事件能改变标准执行流程</font>；而且这些事件主要涉及异常情况。<span lang="EN-US">“</span>命令行实用程序<span lang="EN-US">”</span>是这种传统应用程序的典型例子。 <span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt"><font color="#ff0000">另一类应用程序由外部发生的事件来驱动<span lang="EN-US">——</span>换言之，事件在应用程序之外生成，无法由应用程序或程序员来控制。</font>具体需要执行的代码取决于接收到的事件，或者它相对于其他事件的抵达时间。所以，控制流程既不能是顺序的，也不能是事先设定好的，因为它要依赖于外部事件。事件驱动的<span lang="EN-US">GUI</span>应用程序是这种应用程序的典型例子，它们由命令和选择（也就是用户造成的事件）来驱动。 <span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt">Web</span><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt">应用程序由提交的表单和用户请求的网页来驱动，它们也可划归到上述类别。但是，<span lang="EN-US">GUI</span>应用程序对于接收到的事件仍有一定程度的控制，因为这些事件要依赖于向用户显示的窗口和控件，而窗口和控件是由程序员控制的。<span lang="EN-US">Web</span>应用 程序则不然，因为一旦用户采取不在预料之中的操作（比如使用浏览器的历史记录、手工输入链接以及模拟一次表单提交等等），就很容易打乱设计好的应用程序逻辑。 <span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt">显然，必须采取不同的技术来处理这些情况。它能处理任何顺序的事件，并能提供有意义的响应<span lang="EN-US">——</span>即使这些事件发生的顺序和预计的不同。有限状态机正是为了满足这方面的要求而设计的。 <span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 27pt; line-height: 150%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal" style="line-height: 150%"><span style="font-size: 12pt; color: black; line-height: 150%; font-family: 宋体; letter-spacing: 0.4pt; mso-bidi-font-family: arial; mso-font-kerning: 0pt">　　有限状态机是一种概念性机器，它能采取某种操作来响应一个外部事件。具体采取的操作不仅能取决于接收到的事件，还能取决于各个事件的相对发生顺序。之所以能做到这一点，是因为机器能跟踪一个内部状态，它会在收到事件后进行更新。<font color="#ff0000">为一个事件而响应的行动不仅取决于事件本身，还取决于机器的内部状态。另外，采取 的行动还会决定并更新机器的状态。这样一来，任何逻辑都可建模成一系列事件<span lang="EN-US">/</span>状态组合。</font></span><span lang="EN-US" style="font-size: 12pt; line-height: 150%; font-family: 宋体; mso-bidi-font-family: arial"><o:p></o:p></span></p><img src ="http://www.cppblog.com/woaidongmao/aggbug/69409.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/woaidongmao/" target="_blank">肥仔</a> 2008-12-14 17:53 <a href="http://www.cppblog.com/woaidongmao/archive/2008/12/14/69409.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>有限状态自动机</title><link>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69408.html</link><dc:creator>肥仔</dc:creator><author>肥仔</author><pubDate>Sun, 14 Dec 2008 09:30:00 GMT</pubDate><guid>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69408.html</guid><wfw:comment>http://www.cppblog.com/woaidongmao/comments/69408.html</wfw:comment><comments>http://www.cppblog.com/woaidongmao/archive/2008/12/14/69408.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/woaidongmao/comments/commentRss/69408.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/woaidongmao/services/trackbacks/69408.html</trackback:ping><description><![CDATA[<p>&nbsp;</p> <p>有限状态自动机是具有离散输入和输出的系统的一种数学模型。  <p>　　其主要特点有以下几个方面：  <p>– (1)系统具有有限个状态，不同的状态代表不同的意义。按照实际的需要，系统可以在不同的状态下完成规定的任务。  <p>– (2)我们可以将输入字符串中出现的字符汇集在一起构成一个字母表。系统处理的所有字符串都是这个字母表上的字符串。  <p>– (3)系统在任何一个状态下，从输入字符串中读入一个字符，根据当前状态和读入的这个字符转到新的状态。  <p>– (4)系统中有一个状态，它是系统的开始状态。  <p>– (5)系统中还有一些状态表示它到目前为止所读入的字符构成的字符串是语言的一个句子。  <p>–  <p>　　形式定义  <p>• 定义：有限状态自动机(FA—finite automaton)是一个五元组：  <p>– M=(Q, Σ, δ, q0, F)  <p>• 其中，  <p>– Q——状态的非空有穷集合。∀q∈Q，<font color="#ff0000">q称为M的一个状态</font>。  <p>– Σ——输入字母表。  <p>– δ——状态转移函数，有时又叫作状态转换函数或者移动函数，<font color="#ff0000">δ：Q×Σ→Q，δ(q,a)=p。</font>  <p>– q0——M的开始状态，也可叫作初始状态或启动状态<font color="#ff0000">。q0∈Q。</font>  <p>– F——M的终止状态集合。F被Q包含。任给q∈F，q称为M的终止状态。</p><img src ="http://www.cppblog.com/woaidongmao/aggbug/69408.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/woaidongmao/" target="_blank">肥仔</a> 2008-12-14 17:30 <a href="http://www.cppblog.com/woaidongmao/archive/2008/12/14/69408.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NFA转DFA</title><link>http://www.cppblog.com/woaidongmao/archive/2008/12/13/69344.html</link><dc:creator>肥仔</dc:creator><author>肥仔</author><pubDate>Sat, 13 Dec 2008 07:21:00 GMT</pubDate><guid>http://www.cppblog.com/woaidongmao/archive/2008/12/13/69344.html</guid><wfw:comment>http://www.cppblog.com/woaidongmao/comments/69344.html</wfw:comment><comments>http://www.cppblog.com/woaidongmao/archive/2008/12/13/69344.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/woaidongmao/comments/commentRss/69344.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/woaidongmao/services/trackbacks/69344.html</trackback:ping><description><![CDATA[<p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">一个<span style="background: white; color: red">非确定自动机</span><span lang="EN-US">( NFA) </span>在读入符号串之后，并不确切地知道自动机处于哪个状态。但可以肯定一定处于状态集中的某一状态。该状态集记做<span lang="EN-US"> {q1,q2,…qk} </span>。而一个等价的<span style="color: red">确定自动机</span><span lang="EN-US">( DFA) </span>读入同样的<span lang="EN-US"> w </span>一定处于某个确定的状态上。这样，都是读入同样的<span lang="EN-US"> w </span>，<span lang="EN-US"> DFA </span>到达某一个状态，而<span lang="EN-US"> NFA </span>到达某一个状态集。由<span lang="EN-US"> w </span>的任意性，可将<span lang="EN-US"> NFA </span>的所有的状态集和<span lang="EN-US"> DFA </span>的状态一一对应起来。这种对应的前提就是能识别同样的输入串。即<span lang="EN-US"> L(M1)=L(M2) </span>。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">显然，后一个状态集是依赖于前一个状态集的，是在前一个状态集的基础上，（其内任意结点）经过同一条路径到达的。下面是一个简单的例子：<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">&nbsp;&nbsp; <a href="http://www.cppblog.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/NFADFA_D7D0/clip_image001_2.gif"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="220" alt="clip_image001" src="http://www.cppblog.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/NFADFA_D7D0/clip_image001_thumb.gif" width="399" border="0" v:shapes="_x0000_i1025"></a><o:p></o:p></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">可以看出，其核心是将<span lang="EN-US"> NFA </span>状态集归并为<span lang="EN-US"> DFA </span>中的状态。在<span lang="EN-US"> NFA </span>中，无论是从<span lang="EN-US"> 1 </span>到<span lang="EN-US"> 4 </span>，还是<span lang="EN-US"> 1 </span>到<span lang="EN-US"> 5 </span>，作为集合来讲都是集合<span lang="EN-US"> 1 </span>到集合<span lang="EN-US"> 2 </span>，最为重要得是经过的条件都是<span lang="EN-US"> a </span>。因而从识别语言的效果是一样的。这使得这些弧合并成为可能。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">考虑集合覆盖的情况。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><a href="http://www.cppblog.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/NFADFA_D7D0/clip_image002_2.gif"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="194" alt="clip_image002" src="http://www.cppblog.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/NFADFA_D7D0/clip_image002_thumb.gif" width="392" border="0" v:shapes="_x0000_i1026"></a><o:p></o:p></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">一个结点属于第一个集合又同时属于第二个集合。这种情况不一定好理解。但如果从路径的历史的角度进一步区分，即不同的时间经过同一个结点，将其看成是不同的状态。按照这种时空的角度进一步区分，得到右图。这和图<span lang="EN-US"> 1 </span>是类似的。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">再来看看带有终态结点的情况：<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">&nbsp;&nbsp; <a href="http://www.cppblog.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/NFADFA_D7D0/clip_image003_2.gif"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="133" alt="clip_image003" src="http://www.cppblog.com/images/cppblog_com/woaidongmao/WindowsLiveWriter/NFADFA_D7D0/clip_image003_thumb.gif" width="276" border="0" v:shapes="_x0000_i1027"></a><o:p></o:p></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">ab </span><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">，<span lang="EN-US"> abb </span>均为该<span lang="EN-US"> NFA </span>识别的句子，其转换如下：<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <o:p></o:p></span></p> <table class="MsoNormalTable" style="margin-left: 5.4pt; border-collapse: collapse; mso-padding-alt: 0cm 0cm 0cm 0cm" cellspacing="0" cellpadding="0" border="1"> <tbody> <tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes"> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: windowtext 1pt solid; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 1pt solid; width: 81pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-alt: solid windowtext .5pt" valign="top" width="108"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><o:p>&nbsp;</o:p></span></p></td> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: windowtext 1pt solid; padding-left: 5.4pt; padding-bottom: 0cm; border-left: medium none; width: 90pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: inset #d4d0c8 .75pt" valign="top" width="120"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">I <sub>a</sub><o:p></o:p></span></p></td> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: windowtext 1pt solid; padding-left: 5.4pt; padding-bottom: 0cm; border-left: medium none; width: 90pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: inset #d4d0c8 .75pt" valign="top" width="120"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">I<sub>b</sub><o:p></o:p></span></p></td></tr> <tr style="mso-yfti-irow: 1"> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: medium none; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 1pt solid; width: 81pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: inset #d4d0c8 .75pt" valign="top" width="108"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">A{1,2}<o:p></o:p></span></p></td> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: medium none; padding-left: 5.4pt; padding-bottom: 0cm; border-left: medium none; width: 90pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-left-alt: inset #d4d0c8 .75pt; mso-border-top-alt: inset #d4d0c8 .75pt; mso-border-bottom-alt: solid windowtext .5pt; mso-border-right-alt: solid windowtext .5pt" valign="top" width="120"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">{3}<o:p></o:p></span></p></td> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: medium none; padding-left: 5.4pt; padding-bottom: 0cm; border-left: medium none; width: 90pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-left-alt: inset #d4d0c8 .75pt; mso-border-top-alt: inset #d4d0c8 .75pt; mso-border-bottom-alt: solid windowtext .5pt; mso-border-right-alt: solid windowtext .5pt" valign="top" width="120"> <p class="MsoNormal" style="line-height: 118%; text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; line-height: 118%; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">Φ</span><span lang="EN-US" style="font-size: 12pt; line-height: 118%; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p></td></tr> <tr style="mso-yfti-irow: 2"> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: medium none; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 1pt solid; width: 81pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: inset #d4d0c8 .75pt" valign="top" width="108"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">B{3}<o:p></o:p></span></p></td> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: medium none; padding-left: 5.4pt; padding-bottom: 0cm; border-left: medium none; width: 90pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-left-alt: inset #d4d0c8 .75pt; mso-border-top-alt: inset #d4d0c8 .75pt; mso-border-bottom-alt: solid windowtext .5pt; mso-border-right-alt: solid windowtext .5pt" valign="top" width="120"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">Φ</span><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p></td> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: medium none; padding-left: 5.4pt; padding-bottom: 0cm; border-left: medium none; width: 90pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-left-alt: inset #d4d0c8 .75pt; mso-border-top-alt: inset #d4d0c8 .75pt; mso-border-bottom-alt: solid windowtext .5pt; mso-border-right-alt: solid windowtext .5pt" valign="top" width="120"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">{3,4}<o:p></o:p></span></p></td></tr> <tr style="mso-yfti-irow: 3; mso-yfti-lastrow: yes"> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: medium none; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 1pt solid; width: 81pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: inset #d4d0c8 .75pt" valign="top" width="108"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">C{3,4}<o:p></o:p></span></p></td> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: medium none; padding-left: 5.4pt; padding-bottom: 0cm; border-left: medium none; width: 90pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-left-alt: inset #d4d0c8 .75pt; mso-border-top-alt: inset #d4d0c8 .75pt; mso-border-bottom-alt: solid windowtext .5pt; mso-border-right-alt: solid windowtext .5pt" valign="top" width="120"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">Φ</span><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p></td> <td style="border-right: windowtext 1pt solid; padding-right: 5.4pt; border-top: medium none; padding-left: 5.4pt; padding-bottom: 0cm; border-left: medium none; width: 90pt; padding-top: 0cm; border-bottom: windowtext 1pt solid; mso-border-left-alt: inset #d4d0c8 .75pt; mso-border-top-alt: inset #d4d0c8 .75pt; mso-border-bottom-alt: solid windowtext .5pt; mso-border-right-alt: solid windowtext .5pt" valign="top" width="120"> <p class="MsoNormal" style="text-align: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">{3,4}<o:p></o:p></span></p></td></tr></tbody></table> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">从某种意义上说。<span lang="EN-US"> NFA </span>中的状态<span lang="EN-US"> 3 </span>在<span lang="EN-US"> DFA </span>中被分离成两部分，当首次到达<span lang="EN-US"> 3 </span>时应该是状态<span lang="EN-US"> B </span>，而第二次以后再到达<span lang="EN-US"> 3 </span>则应该属于状态<span lang="EN-US"> C </span>。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">根据规则，<span lang="EN-US"> C{3,4} </span>为<span lang="EN-US"> DFA </span>的终态，但在<span lang="EN-US"> NFA </span>中，只有<span lang="EN-US"> 4 </span>为终态，<span lang="EN-US"> C </span>中仍然有<span lang="EN-US"> 3 </span>为非终态，若有路径<span lang="EN-US"> 1 à 3 à 3 </span>映射到<span lang="EN-US"> DFA </span>中也是<span lang="EN-US"> A à B à C </span>，何解？<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">这里面最关键的是：对任意一个句子，总可以在两个图中分别找到一条路径，形成对应关系。并不是说<span lang="EN-US"> NFA </span>中的每条路径都要和<span lang="EN-US"> DFA </span>中的每条路径一一对应。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">当识别句子<span lang="EN-US"> ab </span>时，选择由<span lang="EN-US"> 3 </span>直接到达<span lang="EN-US"> 4 </span>的路径。当识别句子<span lang="EN-US"> abb </span>时，则在状态<span lang="EN-US"> 3 </span>循环一次再到达<span lang="EN-US"> 4 </span>。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">现在设想，通过<span lang="EN-US"> 1 à 3 à 3 </span>经过的路径也是<span lang="EN-US"> ab </span>。但此时并未到达终态。可以说，在到达<span lang="EN-US"> C </span>中的<span lang="EN-US"> 3 </span>时，必然选择了两个<span lang="EN-US"> b </span>以上的句子。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">而这样的路径与选择句子有关系。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">对于<span lang="EN-US"> NFA </span>能识别的句子，在<span lang="EN-US"> DFA </span>中也能识别。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal" style="text-indent: 21pt; text-align: left; mso-pagination: widow-orphan" align="left"><span style="font-size: 12pt; font-family: 宋体; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">对于<span lang="EN-US"> NFA </span>不能识别的句子，在<span lang="EN-US"> DFA </span>中也不能识别。<span lang="EN-US"><o:p></o:p></span></span></p> <p class="MsoNormal"><span lang="EN-US" style="font-size: 12pt; font-family: arial"><o:p>&nbsp;</o:p></span></p><img src ="http://www.cppblog.com/woaidongmao/aggbug/69344.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/woaidongmao/" target="_blank">肥仔</a> 2008-12-13 15:21 <a href="http://www.cppblog.com/woaidongmao/archive/2008/12/13/69344.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>