有限状态机详解(转载)
以前总觉得有限状态机和无限状态机非常的难理解,原来也就是自己一直没有一个直观的认识,今天看到一篇博客,总算对有限状态机入门了。一看就懂。
转载地址:http://blog.csdn.net/zqixiao_09/article/details/50239337
我们知道,一般编写程序时都要画出流程图,按照流程图结构来编程,如果编写一个比较繁琐,容易思维混乱的程序时,我们可以利用有限状态机模型画出一个状态转移图,这样便可以利用画出的逻辑图来编写程序,简洁且不易出错。
那什么是有限状态机是什么意思呢?百度百科上这样解释:
有限状态机,(英语:Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
常见的计算机就是使用有限状态机作为计算模型的:对于内存的不同状态,CPU通过读取内存值进行计算,更新内存中的状态。CPU还通过消息总线接受外部输入设备(如键盘、鼠标)的指令,计算后更改内存中的状态,计算结果输出到外部显示设备(如显示器),以及持久化存储在硬盘。
电脑游戏设计中也经常使用有限状态机模型。以水果忍者游戏为例,游戏中水果的状态是有限状态,其运行轨迹是由模拟物理运动规律的计算公式运算而成的,一个香蕉抛起来后会按照抛物线运行,其每一帧位置变化都是一个状态的改变,状态改变通过计算公式来决定。当然作为游戏不会仅仅这么简单,如果这么简单就是动画了,游戏还有复杂的人机交互事件,比如用手在屏幕上“切”了水果,水果感知到这个事件后,会按照程序逻辑进入爆炸状态。
简单知道其定义是没有用的,在C编程中我们改如何应用呢?
例题1:去除一个字符串中连续的空格,即 H__el___lo 变成 H_el_lo;
我们拿到这道题时,可能会想到用getchar()依次取字符,当遇到空格时,继续下一个字符是否为空格,如果是则删除,如果不是则继续;那么问题来了,如果空格后面还有空格呢?如果还有很多空格呢?如果还有其他要求呢?这样很容易造成思维混乱,所以这时我们可以用到有限状态机模型,好像这样说很模糊啊,该怎么使用呢?利用这种方式,最后的就是利用好flag,即标识符;这样吧,我们来画一下这个模型:
这样看好像有点抽象,我来解释一下:
状态0 (flag = 1)若当前字符是空格,输出并跳转到状态1(flag = 1),如果是非空格,则打印字符;
状态1 (flag = 1) 若当前字符为非空格,则输出并跳转到状态0,若是空格,则不打印;
下面几个例题我尽量写得清楚;
我们按照这种逻辑来写程序,看看是不是比较方便,代码如下:
#include <stdio.h> int main(int argc, char *argv[])
{ char flag = 0; int ch; while((ch = getchar()) != EOF) { switch(flag) { case 0: if(ch == ' ') flag = 1; putchar(ch); break; case 1: if(ch == ' ') continue; flag = 0; putchar(ch); break; default: break; } } return 0;
}
程序执行结果如下:
[cpp] view plain copy
fs@ubuntu:~/qiang/char1$ gcc -o test test.c
fs@ubuntu:~/qiang/char1$ ./test
H el lo
H el lo
效果很明显;
上面的例题还算简单吧,好像不用这个模型也可以是吧,那好,我们再来一题
例题2:除连续的空格但字符串中的连续空格不变,比如H_ _el__"wor___ld"___lo;
大家看看,如果直接写程序是不是很烦,一会就乱掉了,那我们还用这个模型来编写,还是先画图:
#include <stdio.h> int main(int argc, char *argv[])
{ char flag = 0; int ch; while((ch = getchar()) != EOF){ switch(flag){ case 0: if(ch == ' ') flag = 1; if(ch == '"') flag = 2; putchar(ch); break; case 1: if(ch != ' '){ flag = 0; if(ch == '"') flag = 2; putchar(ch); } break; case 2: if(ch == '"') flag = 0; if(ch == '\"') ch = '"'; putchar(ch); break; default: printf("error!\n"); break; } } return 0;
}
执行结果如下:
[cpp] view plain copy
fs@ubuntu:~/qiang/char1$ ./char3
H el "wor ld" lo
H el "wor ld" lo
来个实际点的问题:
例题3::除单行注释
示例程序:
/*******this is program*****/
#include <stdio.h>
int main()
{ printf("Hello world!\n");//Hello world!
}
将这段程序中的单行注释去掉,继续画图:
编写程序如下:
#include <stdio.h> int main(void)
{ char flag = 0; char ch; while((ch = getchar()) != EOF) { switch(flag) { case 0: if(ch == '/') flag = 1; else putchar(ch); break; case 1: if(ch == '/'){ flag = 2; } else{ putchar('/'); putchar(ch); } break; case 2: if(ch == '\n') { flag = 0; putchar(ch); } break; default: break; } }
}
执行命令:
fs@ubuntu:~/qiang/char1$ ./quzhushi1 < test.c >result.c
注意命令中用到的重定向,将test.c文件重定向到quzhushi1 中,输出结果重定向到 result.c中
可查看result.c中:
/*******this is program*****/
#include <stdio.h>
int main()
{ printf("Hello world!\n");//Hello world!
}
单行注释被删除。
例题4:去除单行注释和多行注释
还是这个测试程序:
/*******this is program*****/
#include <stdio.h>
int main()
{ printf("Hello world!\n");//Hello world!
}
好吧,继续画图,图上比较抽象,大家可以自己思考一下
这次比较复杂,要有5个标识符
测试代码如下:
#include <stdio.h> int main()
{ char ch; int flag = 0; while((ch = getchar()) != EOF) { switch(flag) { case 0: if(ch == '/') flag = 1; else putchar(ch); break; case 1: if(ch == '/') flag = 2; else if(ch == '*') flag = 3; else { flag = 0; putchar('/'); putchar(ch); } break; case 2: if(ch == '\n') { putchar(ch); flag = 0; } break; case 3: if(ch == '*') flag = 4; break; case 4: if(ch == '/') flag = 0; else flag = 3; break; } }
}
执行命令:
fs@ubuntu:~/qiang/char1$ ./quzhushi < test.c >result.c
执行结果:
/*******this is program*****/
#include <stdio.h>
int main()
{ printf("Hello world!\n");//Hello world!
}
大家可以比较画的图与所写程序,这种方法可以使我们编写程序时有个很好的思路!
有限状态机详解(转载)相关推荐
- spring依赖注入原理详解(转载)
spring依赖注入原理详解----转载 所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中.当spring容器启动后,spring容器初始化,创建并管理bean对象,以及销毁它.所 ...
- Unity下FSM有限状态机详解
FSM有限状态机详解 文章目录 FSM有限状态机详解 FSM的定义 FSM的适用性 FSM的设计分析 状态转换表的使用 状态和条件的标识符Id 条件基类的设计 FSMTrigger 状态基类的设计 F ...
- Executor框架的详解(转载)
在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源.同时,为每一个任务创建一个新线程来执行 ...
- vsftpd配置文件详解[转载]
vsftpd配置文件详解 1.默认配置: 1>允许匿名用户和本地用户登陆. anonymous_enable=YES local_enable=YES 2>匿名用户使用 ...
- SqlHelper详解(转载)
SqlHelper 类实现详细信息 SqlHelper 类用于通过一组静态方法来封装数据访问功能.该类不能被继承或实例化,因此将其声明为包含专用构造函数的不可继承类. 在 SqlHelper 类中实现 ...
- HTTP详解(转载)
HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统.它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展.目前在WWW中使用的是HTTP/1.0的第 ...
- ntp 配置详解(转载后整理汇总)
ntp 配置详解 一.时间和时区 在LINUX系统中,有许多场合都使用时间戳的方式表示时间,即从1970年1月1日起至当前的天数或秒数. 世界遵循一个标准UTC,中国的标准是CST(北京时间)中国处于 ...
- 分布式事务中的三种解决方案详解(转载)
一.分布式事务前奏 快看小说网事务:事务是由一组操作构成的可靠的独立的工作单元,事务具备ACID的特性,即原子性.一致性.隔离性和持久性. 本地事务:当事务由资源管理器本地管理时被称作本地事务.本地事 ...
- SpringMVC 之类型转换Converter详解转载
SpringMVC之类型转换Converter详解 本文转载 http://www.tuicool.com/articles/uUjaum 1.1 目录 1.1 目录 1.2 ...
- VLAN原理详解[转载] 网桥--交换机---路由器
来自:http://blog.csdn.net/phunxm/article/details/9498829 一.什么是桥接 桥接工作在OSI网络参考模型的第二层数据链路层,是一种以MAC地址来作 ...
最新文章
- 自动红眼移除算法 附c++完整代码
- lucene join解决父子关系索引
- 【常见笔试面试算法题12续集五】动态规划算法案例分析5 01背包练习题
- Android基础控件之Button的基本使用
- CSS(网页样式语言)基础,网页CSS设计样式基础知识点 小白教程
- 如何使用命令行 群晖_群晖Nas系统篇:拿回root账户权限,适用6.2及以上(7.0)系统...
- 自治系统间的路由协议--BGP详细介绍
- 打造利器Qt Creator:代码todo工具的使用
- 操作系统 第二部分 进程管理(一)
- 是否可以从头来过——时间旅行为什么不可能
- python 知乎美女_听说知乎大神用python爬取高颜值美女,是怎么操作的?
- Java Web程序设计笔记 • 【第1章 Web应用程序】
- react实现简单的计算器功能。
- php 手机版 答题系统,基于ThinkPHP框架开发的驾考在线答题系统_WAP手机自适应界面+手机在线驾考宝典答题系统...
- 算法——从9个硬币中找出其中的1枚假硬币
- 洛谷P1605 迷宫(dfs) 题解
- CSS3 transform 2D变幻,过渡
- HTML黑白触摸变彩色,使用HTML5 转换彩色图片为黑白色知识讲解.doc
- acer 4750 Fn+亮度键(左右方向键亮度调节)无效问题的解决办法
- 三剑客 文本处理awk
热门文章
- 计算机作品三等奖——飞翔吧,七彩的梦
- 现在的AI专业(如机器学习)已经沦为调包专业了吗?
- IntelliJ IDEA设置显示内存指示器的几种方法
- php 微信获取门店列表,【转载】微信公众号获取用户地理位置并列出附近的门店...
- N32905音视频学习笔记-录音和播放
- eas报错日记_EAS8.1预留记录查询报错
- 星球日报 | 人民日报微博评ofo押金换金币:别玩虚的;Mt.Gox将赔付1.76万亿日元系谣言;...
- python入门笔记(1)
- 图标设计原则_图标设计的7个原则
- 微软e5服务器,微软发布Office 365 E5计划