Linux C 编程技巧--利用有限状态机模型编程

我们知道,一般编写程序时都要画出流程图,按照流程图结构来编程,如果编写一个比较繁琐,容易思维混乱的程序时,我们可以利用有限状态机模型画出一个状态转移图,这样便可以利用画出的逻辑图来编写程序,简洁且不易出错。

      那什么是有限状态机是什么意思呢?百度百科上这样解释:

      有限状态机,(英语: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,若是空格,则不打印;
下面几个例题我尽量写得清楚;
我们按照这种逻辑来写程序,看看是不是比较方便,代码如下:
[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main(int argc, char *argv[])  
  4. {  
  5.     char flag = 0;  
  6.     int ch;  
  7.     while((ch = getchar()) != EOF)  
  8.     {  
  9.         switch(flag)  
  10.         {  
  11.         case 0:  
  12.             if(ch == ' ')  
  13.                 flag = 1;  
  14.             putchar(ch);  
  15.             break;  
  16.         case 1:  
  17.             if(ch == ' ')  
  18.                 continue;  
  19.             flag = 0;  
  20.             putchar(ch);  
  21.             break;  
  22.         default:  
  23.             break;  
  24.         }  
  25.     }  
  26.     return 0;  
  27. }  

程序执行结果如下:
[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/char1$ gcc -o test test.c   
  2. fs@ubuntu:~/qiang/char1$ ./test  
  3. H  el   lo  
  4. H el lo  
效果很明显;
上面的例题还算简单吧,好像不用这个模型也可以是吧,那好,我们再来一题
例题2:除连续的空格但字符串中的连续空格不变,比如H_ _el__"wor___ld"___lo;
大家看看,如果直接写程序是不是很烦,一会就乱掉了,那我们还用这个模型来编写,还是先画图:
[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main(int argc, char *argv[])  
  4. {  
  5.     char flag = 0;  
  6.     int ch;  
  7.     while((ch = getchar()) != EOF){  
  8.         switch(flag){  
  9.         case 0:  
  10.             if(ch == ' ')  
  11.                 flag = 1;  
  12.             if(ch == '"')  
  13.                 flag = 2;  
  14.             putchar(ch);  
  15.             break;  
  16.         case 1:  
  17.             if(ch != ' '){  
  18.                 flag = 0;  
  19.                 if(ch == '"')  
  20.                     flag = 2;  
  21.                 putchar(ch);  
  22.             }  
  23.             break;  
  24.         case 2:  
  25.             if(ch == '"')  
  26.                 flag = 0;  
  27.             if(ch == '\"')  
  28.                 ch = '"';  
  29.             putchar(ch);  
  30.             break;  
  31.         default:  
  32.             printf("error!\n");  
  33.             break;  
  34.         }  
  35.     }  
  36.     return 0;  
  37. }  

执行结果如下:
[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/char1$ ./char3  
  2. H  el  "wor   ld"   lo  
  3. H el "wor   ld" lo  

 
来个实际点的问题:
例题3::除单行注释
示例程序:
[cpp] view plaincopy
  1. /*****This is a program!*****/  
  2. #include <stdio.h>  
  3.   
  4. int main()  
  5. {  
  6.     printf("Hello world!\n");//Hello world!  
  7. }  

将这段程序中的单行注释去掉,继续画图:
编写程序如下:
[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main(void)  
  4. {  
  5.     char flag = 0;  
  6.     char ch;  
  7.     while((ch = getchar()) != EOF)  
  8.     {  
  9.         switch(flag)  
  10.         {  
  11.         case 0:  
  12.             if(ch == '/')  
  13.                 flag = 1;  
  14.             else  
  15.                 putchar(ch);  
  16.             break;  
  17.         case 1:  
  18.             if(ch == '/'){  
  19.                 flag = 2;  
  20.             }  
  21.             else{  
  22.                 putchar('/');  
  23.                 putchar(ch);  
  24.             }  
  25.             break;  
  26.         case 2:  
  27.             if(ch == '\n')  
  28.             {  
  29.                 flag = 0;  
  30.                 putchar(ch);  
  31.             }  
  32.             break;  
  33.         default:  
  34.             break;  
  35.         }  
  36.   
  37.     }  
  38. }  

执行命令:
[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/char1$ ./quzhushi1 < test.c >result.c  

注意命令中用到的重定向,将test.c文件重定向到quzhushi1 中,输出结果重定向到 result.c中
可查看result.c中:
[cpp] view plaincopy
  1. /*****This is a program!*****/  
  2. #include <stdio.h>  
  3.   
  4. int main()  
  5. {  
  6.     printf("Hello world!\n");  
  7. }  

单行注释被删除。
例题4:去除单行注释和多行注释
还是这个测试程序:
[cpp] view plaincopy
  1. /*****This is a program!*****/  
  2. #include <stdio.h>  
  3.   
  4. int main()  
  5. {  
  6.     printf("Hello world!\n");//Hello world!  
  7. }  

好吧,继续画图,图上比较抽象,大家可以自己思考一下
这次比较复杂,要有5个标识符
测试代码如下:
[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main()  
  4. {  
  5.     char ch;  
  6.     int flag = 0;  
  7.     while((ch = getchar()) != EOF)  
  8.     {  
  9.         switch(flag)  
  10.         {  
  11.             case 0:  
  12.                 if(ch == '/')  
  13.                     flag = 1;  
  14.                 else  
  15.                     putchar(ch);  
  16.                 break;  
  17.       
  18.             case 1:  
  19.                 if(ch == '/')  
  20.                     flag = 2;  
  21.                 else if(ch == '*')  
  22.                     flag = 3;  
  23.                 else  
  24.                 {  
  25.                     flag = 0;  
  26.                     putchar('/');  
  27.                     putchar(ch);  
  28.                 }  
  29.                 break;  
  30.           
  31.             case 2:  
  32.                 if(ch == '\n')  
  33.                 {  
  34.                     putchar(ch);  
  35.                     flag = 0;  
  36.                 }  
  37.                 break;  
  38.   
  39.             case 3:  
  40.                 if(ch == '*')  
  41.                     flag = 4;  
  42.                 break;  
  43.   
  44.             case 4:  
  45.                 if(ch == '/')  
  46.                     flag = 0;  
  47.                 else  
  48.                     flag = 3;  
  49.                 break;  
  50.         }  
  51.     }  
  52. }  

执行命令:
[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/char1$ ./quzhushi < test.c >result.c  

执行结果:
result.c
[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main()  
  4. {  
  5.     printf("Hello world!\n");  
  6. }  

大家可以比较画的图与所写程序,这种方法可以使我们编写程序时有个很好的思路!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402526.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM的垃圾回收机制

发现一篇好文章,能够快速的帮助我们理清楚思路,以下内容转载 JVM的内部结构 先说下jvm运行时数据的划分&#xff0c;粗暴的分可以分为堆区(Heap)和栈区(Stack)&#xff0c;但jvm的分法实际上比这复杂得多&#xff0c;大概分为下面几块&#xff1a; 1、程序计数器(Program Conut…

centos配置ssh免密码登录后仍要输入密码的解决方法

From: https://blog.csdn.net/zwbill/article/details/80448939 前言 在搭建Linux集群服务的时候&#xff0c;主服务器需要启动从服务器的服务&#xff0c;如果通过手动启动&#xff0c;集群内服务器几台还好&#xff0c;要是像阿里1000台的云梯Hadoop集群的话&#xff0c;轨…

Linux c学习--从标准输入输出看流和缓冲区

学习标准输入输出&#xff0c;我们都会遇到一个概念&#xff0c;流和缓冲区&#xff0c;但到底什么是流&#xff0c;什么是缓冲区呢&#xff1f; 书《C Primer Plus》上说&#xff0c;C程序处理一个流而不是直接处理文件。后面的解释十分抽象&#xff1a;『流&#xff08;strea…

ubuntu vim8.0源码安装

安装篇 从https://github.com/vim/vim下载相应zip源码文件&#xff0c;利用unzip vim-master.zip 命令解压到当前用户目录&#xff0c;即~&#xff1b; 解压后进入vim的src目录&#xff0c;首先&#xff0c;即运行 sudo apt-get updata &#xff08;更新系统软件源&#xff09;…

Linux C学习---递归函数

最近学习到了递归&#xff0c;刚开始看&#xff0c;真是头大&#xff0c;函数里面嵌套其本身&#xff0c;到底是怎么个流程啊&#xff1f; 现在&#xff0c;咱们先了解下递归函数的数学原理&#xff1a; 高中的时候就出现很多递归函数&#xff0c;应该是在“级数”那里的习题中…

Java判断两个Date是不是同一天

From: https://blog.csdn.net/xingchenbingbuyu/article/details/82734695 Java判断两个Date是不是同一天 1. 利用Calendar Calendar cal1 Calendar.getInstance(); Calendar cal2 Calendar.getInstance(); cal1.setTime(date1); cal2.setTime(date2); boolean sameDay ca…

js二级下拉被flash档住的解决办法

在<object></object>及以内的代码加入到<script>标签对内<script language"javascript" type"text/javascript"> <param name"wmode" value"transparent" />//背景透明 </script> 转载于:https:/…

iOS:以前笔记,未整理版。太多了,先放着吧。。。。。。。

1、**************************************************************** 单例共享 **************************************************************** 单例 共享信息 .m static OneT *newone nil; (instancetype)shalldata { if (newone nil) { newone [[OneT alloc]init]…

C语言经典编程题--哥德巴赫猜想 、完数 、猜数字等

一、 验证歌德巴赫猜想&#xff1a;任意一个不小于6的偶数都可以表示成两个素数的和。从键盘任意给一个符合条件的数&#xff0c;输出相应的两个素数。 素数&#xff1a;指在一个大于1的自然数中&#xff0c;除了1和此整数自身外&#xff0c;没法被其他自然数整除的数 代码如下…

Calendar的DAY_OF_MONTH, DAY_OF_YEAR, DATE的区别

From: https://blog.csdn.net/weixin_34233679/article/details/87286628 cal1.add(Calendar.DAY_OF_MONTH,1); cal1.add(Calendar.DAY_OF_YEAR,1); cal1.add(Calendar.DATE,1); 就单纯的add操作结果都一样&#xff0c;因为都是将日期1 就没有区别说是在月的日期中加1还是…

Cisco网络防火墙配置方法

这篇文章主要介绍了Cisco网络防火墙配置方法,需要的朋友可以参考下  由于网络防火墙默认禁止所有的通信&#xff0c;因为&#xff0c;只有对其进行适当配置后&#xff0c;才能实现正常的网络通信。  如何配置Cisco网络防火墙  1.进入全局配置模式  ciscoasa#configure …

linux远程登录三种方式telnet,ssh,vnc

linux远程连接三种方式telnet&#xff0c;ssh&#xff0c;vnctelnet和ssh服务只能实现基于字符界面的远程控制&#xff0c;如果要基于图形界面进行远程控制&#xff0c;可以借助免费的VNC来完成。一、telnet连接1.首先进入终端&#xff0c;查看是否安装了telnet服务。linux默认…

老司机学习MyBatis之如何通过select返回Map

From: https://blog.csdn.net/Gaomb_1990/article/details/80638177 一、案例 当要查询的结果是一个Map的时候&#xff0c;这里分为两种情况&#xff1a; ①返回单条记录 <select id"getUserByIdReturnMap" resultType"map"> select id, log…

大数据之Yarn——Capacity调度器概念以及配置

试想一下&#xff0c;你现在所在的公司有一个hadoop的集群。但是A项目组经常做一些定时的BI报表&#xff0c;B项目组则经常使用一些软件做一些临时需求。那么他们肯定会遇到同时提交任务的场景&#xff0c;这个时候到底如何分配资源满足这两个任务呢&#xff1f;是先执行A的任务…

C#基于Socket的简单聊天室实践

序&#xff1a;实现一个基于Socket的简易的聊天室&#xff0c;实现的思路如下&#xff1a; 程序的结构&#xff1a;多个客户端一个服务端&#xff0c;客户端都是向服务端发送消息&#xff0c;然后服务端转发给所有的客户端&#xff0c;这样形成一个简单的聊天室功能。 实现的细…

C/C++经典面试题

面试题1&#xff1a;变量的声明和定义有什么区别 为变量分配地址和存储空间的称为定义&#xff0c;不分配地址的称为声明。一个变量可以在多个地方声明&#xff0c;但只能在一个地方定义。加入extern修饰的是变量的声明&#xff0c;说明此变量将在文件以外或在文件后面部分定义…

Java跳出多重循环

From: https://www.cnblogs.com/fastfn/p/9777067.html 场景&#xff1a;很多的时候需要做到跳出多重循环&#xff0c;而在Java中虽然后goto关键字&#xff0c;但是是保留字&#xff0c;并没有启用。而在处理分支结构的if...else,switch...case,好像都达不到想要的效果。 作为…

CCF 节日

问题描述有一类节日的日期并不是固定的&#xff0c;而是以“a月的第b个星期c”的形式定下来的&#xff0c;比如说母亲节就定为每年的五月的第二个星期日。现在&#xff0c;给你a&#xff0c;b&#xff0c;c和y1, y2(1850 ≤ y1, y2 ≤ 2050)&#xff0c;希望你输出从公元y1年到…

回文数算法

1、回文数&#xff1a;一种数字&#xff0c;如&#xff1a;12321, 这个数字正读是12321&#xff0c;倒读也是12321&#xff0c;即&#xff1a;将这个数的数字按相反的顺序重新排列后&#xff0c;所得到的数和原来的数一样。回文数判别算法&#xff08;java实现&#xff09;impo…

深入了解scanf() getchar()和gets()等函数之间的区别

----------------------------------------------------| 问题描述一&#xff1a;&#xff08;分析scanf()和getchar()读取字符&#xff09; |-------------------------------------------------- scanf(), getchar()等都是标准输入函数&#xff0c;一般人都会觉得这几个函数…